diff options
Diffstat (limited to 'components/script')
109 files changed, 3054 insertions, 769 deletions
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 23873d59767..d7032f290e0 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -91,6 +91,7 @@ script_plugins = {path = "../script_plugins"} script_traits = {path = "../script_traits"} selectors = { path = "../selectors" } serde = {version = "1", features = ["derive"]} +serde_bytes = "0.11" servo_allocator = {path = "../allocator"} servo_arc = {path = "../servo_arc"} servo_atoms = {path = "../atoms"} @@ -112,7 +113,8 @@ utf-8 = "0.7" uuid = {version = "0.8", features = ["v4"]} xml5ever = "0.16" webdriver = "0.40" -webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} +webgpu = {path = "../webgpu"} +webrender_api = {git = "https://github.com/servo/webrender"} webvr_traits = {path = "../webvr_traits"} webxr-api = {git = "https://github.com/servo/webxr", features = ["ipc"]} diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs index 90630fb07c3..7c9ad232f30 100644 --- a/components/script/canvas_state.rs +++ b/components/script/canvas_state.rs @@ -24,6 +24,7 @@ use crate::dom::imagedata::ImageData; use crate::dom::node::{Node, NodeDamage}; use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope; use crate::dom::textmetrics::TextMetrics; +use crate::euclidext::Size2DExt; use crate::unpremultiplytable::UNPREMULTIPLY_TABLE; use canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg}; use canvas_traits::canvas::{CompositionOrBlending, FillOrStrokeStyle, FillRule}; @@ -46,13 +47,14 @@ use net_traits::request::CorsSettings; use pixels::PixelFormat; use profile_traits::ipc as profiled_ipc; use script_traits::ScriptMsg; +use serde_bytes::ByteBuf; use servo_url::{ImmutableOrigin, ServoUrl}; use std::cell::Cell; use std::fmt; use std::str::FromStr; use std::sync::Arc; -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(Clone, JSTraceable, MallocSizeOf)] #[allow(dead_code)] pub(crate) enum CanvasFillOrStrokeStyle { @@ -61,7 +63,7 @@ pub(crate) enum CanvasFillOrStrokeStyle { Pattern(Dom<CanvasPattern>), } -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(Clone, JSTraceable, MallocSizeOf)] pub(crate) struct CanvasContextState { global_alpha: f64, @@ -102,7 +104,7 @@ impl CanvasContextState { } } -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(JSTraceable, MallocSizeOf)] pub(crate) struct CanvasState { #[ignore_malloc_size_of = "Defined in ipc-channel"] @@ -180,6 +182,19 @@ impl CanvasState { .unwrap() } + // https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions + pub fn set_bitmap_dimensions(&self, size: Size2D<u64>) { + self.reset_to_initial_state(); + self.ipc_renderer + .send(CanvasMsg::Recreate(size, self.get_canvas_id())) + .unwrap(); + } + + pub fn reset_to_initial_state(&self) { + self.saved_states.borrow_mut().clear(); + *self.state.borrow_mut() = CanvasContextState::new(); + } + fn create_drawable_rect(&self, x: f64, y: f64, w: f64, h: f64) -> Option<Rect<f32>> { if !([x, y, w, h].iter().all(|val| val.is_finite())) { return None; @@ -300,7 +315,7 @@ impl CanvasState { } } - pub fn get_rect(&self, canvas_size: Size2D<u32>, rect: Rect<u32>) -> Vec<u8> { + pub fn get_rect(&self, canvas_size: Size2D<u64>, rect: Rect<u64>) -> Vec<u8> { assert!(self.origin_is_clean()); assert!(Rect::from_size(canvas_size).contains_rect(&rect)); @@ -491,7 +506,7 @@ impl CanvasState { let smoothing_enabled = self.state.borrow().image_smoothing_enabled; self.send_canvas_2d_msg(Canvas2dMsg::DrawImage( - Some(image_data.into()), + Some(ByteBuf::from(image_data)), image_size, dest_rect, source_rect, @@ -1019,7 +1034,7 @@ impl CanvasState { // https://html.spec.whatwg.org/multipage/#dom-context-2d-getimagedata pub fn get_image_data( &self, - canvas_size: Size2D<u32>, + canvas_size: Size2D<u64>, global: &GlobalScope, sx: i32, sy: i32, @@ -1038,7 +1053,7 @@ impl CanvasState { } let (origin, size) = adjust_size_sign(Point2D::new(sx, sy), Size2D::new(sw, sh)); - let read_rect = match pixels::clip(origin, size, canvas_size) { + let read_rect = match pixels::clip(origin, size.to_u64(), canvas_size) { Some(rect) => rect, None => { // All the pixels are outside the canvas surface. @@ -1057,7 +1072,7 @@ impl CanvasState { // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata pub fn put_image_data( &self, - canvas_size: Size2D<u32>, + canvas_size: Size2D<u64>, imagedata: &ImageData, dx: i32, dy: i32, @@ -1078,7 +1093,7 @@ impl CanvasState { #[allow(unsafe_code)] pub fn put_image_data_( &self, - canvas_size: Size2D<u32>, + canvas_size: Size2D<u64>, imagedata: &ImageData, dx: i32, dy: i32, @@ -1106,7 +1121,7 @@ impl CanvasState { Point2D::new(dirty_x, dirty_y), Size2D::new(dirty_width, dirty_height), ); - let src_rect = match pixels::clip(src_origin, src_size, imagedata_size) { + let src_rect = match pixels::clip(src_origin, src_size.to_u64(), imagedata_size.to_u64()) { Some(rect) => rect, None => return, }; diff --git a/components/script/document_loader.rs b/components/script/document_loader.rs index ef60cbc2a6c..9e103b91380 100644 --- a/components/script/document_loader.rs +++ b/components/script/document_loader.rs @@ -29,7 +29,7 @@ pub enum LoadType { /// created via DocumentLoader::fetch_async) are always removed by the time /// that the owner is destroyed. #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct LoadBlocker { /// The document whose load event is blocked by this object existing. doc: Dom<Document>, diff --git a/components/script/dom/abstractworkerglobalscope.rs b/components/script/dom/abstractworkerglobalscope.rs index e8e41375b70..d5186f91845 100644 --- a/components/script/dom/abstractworkerglobalscope.rs +++ b/components/script/dom/abstractworkerglobalscope.rs @@ -81,32 +81,27 @@ impl ScriptPort for Receiver<DedicatedWorkerScriptMsg> { } pub trait WorkerEventLoopMethods { - type TimerMsg: Send; type WorkerMsg: QueuedTaskConversion + Send; type Event; - fn timer_event_port(&self) -> &Receiver<Self::TimerMsg>; fn task_queue(&self) -> &TaskQueue<Self::WorkerMsg>; fn handle_event(&self, event: Self::Event); fn handle_worker_post_event(&self, worker: &TrustedWorkerAddress) -> Option<AutoWorkerReset>; fn from_worker_msg(&self, msg: Self::WorkerMsg) -> Self::Event; - fn from_timer_msg(&self, msg: Self::TimerMsg) -> Self::Event; fn from_devtools_msg(&self, msg: DevtoolScriptControlMsg) -> Self::Event; } // https://html.spec.whatwg.org/multipage/#worker-event-loop -pub fn run_worker_event_loop<T, TimerMsg, WorkerMsg, Event>( +pub fn run_worker_event_loop<T, WorkerMsg, Event>( worker_scope: &T, worker: Option<&TrustedWorkerAddress>, ) where - TimerMsg: Send, WorkerMsg: QueuedTaskConversion + Send, - T: WorkerEventLoopMethods<TimerMsg = TimerMsg, WorkerMsg = WorkerMsg, Event = Event> + T: WorkerEventLoopMethods<WorkerMsg = WorkerMsg, Event = Event> + DerivedFrom<WorkerGlobalScope> + DerivedFrom<GlobalScope> + DomObject, { let scope = worker_scope.upcast::<WorkerGlobalScope>(); - let timer_event_port = worker_scope.timer_event_port(); let devtools_port = match scope.from_devtools_sender() { Some(_) => Some(scope.from_devtools_receiver()), None => None, @@ -117,7 +112,6 @@ pub fn run_worker_event_loop<T, TimerMsg, WorkerMsg, Event>( task_queue.take_tasks(msg.unwrap()); worker_scope.from_worker_msg(task_queue.recv().unwrap()) }, - recv(timer_event_port) -> msg => worker_scope.from_timer_msg(msg.unwrap()), recv(devtools_port.unwrap_or(&crossbeam_channel::never())) -> msg => worker_scope.from_devtools_msg(msg.unwrap()), }; @@ -132,13 +126,10 @@ pub fn run_worker_event_loop<T, TimerMsg, WorkerMsg, Event>( // Batch all events that are ready. // The task queue will throttle non-priority tasks if necessary. match task_queue.try_recv() { - Err(_) => match timer_event_port.try_recv() { - Err(_) => match devtools_port.map(|port| port.try_recv()) { - None => {}, - Some(Err(_)) => break, - Some(Ok(ev)) => sequential.push(worker_scope.from_devtools_msg(ev)), - }, - Ok(ev) => sequential.push(worker_scope.from_timer_msg(ev)), + Err(_) => match devtools_port.map(|port| port.try_recv()) { + None => {}, + Some(Err(_)) => break, + Some(Ok(ev)) => sequential.push(worker_scope.from_devtools_msg(ev)), }, Ok(ev) => sequential.push(worker_scope.from_worker_msg(ev)), } diff --git a/components/script/dom/audiocontext.rs b/components/script/dom/audiocontext.rs index 2b6f04b421b..0212cb78ffa 100644 --- a/components/script/dom/audiocontext.rs +++ b/components/script/dom/audiocontext.rs @@ -20,6 +20,8 @@ use crate::dom::bindings::num::Finite; use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::DomRoot; +use crate::dom::htmlmediaelement::HTMLMediaElement; +use crate::dom::mediaelementaudiosourcenode::MediaElementAudioSourceNode; use crate::dom::promise::Promise; use crate::dom::window::Window; use crate::task_source::TaskSource; @@ -97,6 +99,10 @@ impl AudioContext { self.context.resume(); } } + + pub fn base(&self) -> DomRoot<BaseAudioContext> { + DomRoot::from_ref(&self.context) + } } impl AudioContextMethods for AudioContext { @@ -240,6 +246,16 @@ impl AudioContextMethods for AudioContext { // Step 6. promise } + + /// https://webaudio.github.io/web-audio-api/#dom-audiocontext-createmediaelementsource + fn CreateMediaElementSource( + &self, + media_element: &HTMLMediaElement, + ) -> Fallible<DomRoot<MediaElementAudioSourceNode>> { + let global = self.global(); + let window = global.as_window(); + MediaElementAudioSourceNode::new(window, self, media_element) + } } impl From<AudioContextLatencyCategory> for LatencyCategory { diff --git a/components/script/dom/bindings/callback.rs b/components/script/dom/bindings/callback.rs index 6059d75605a..fe6b8d36d39 100644 --- a/components/script/dom/bindings/callback.rs +++ b/components/script/dom/bindings/callback.rs @@ -40,7 +40,7 @@ pub enum ExceptionHandling { /// A common base class for representing IDL callback function and /// callback interface types. #[derive(JSTraceable)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct CallbackObject { /// The underlying `JSObject`. callback: Heap<*mut JSObject>, @@ -131,7 +131,7 @@ pub trait CallbackContainer { /// A common base class for representing IDL callback function types. #[derive(JSTraceable, PartialEq)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct CallbackFunction { object: CallbackObject, } @@ -159,7 +159,7 @@ impl CallbackFunction { /// A common base class for representing IDL callback interface types. #[derive(JSTraceable, PartialEq)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct CallbackInterface { object: CallbackObject, } diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf index 3ff7a044b53..5dc9e141ed5 100644 --- a/components/script/dom/bindings/codegen/Bindings.conf +++ b/components/script/dom/bindings/codegen/Bindings.conf @@ -134,7 +134,10 @@ DOMInterfaces = { 'XR': { 'inCompartments': ['SupportsSessionMode', 'RequestSession'], -} +}, +'GPU': { + 'inCompartments': ['RequestAdapter'], +} } diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 86ee03d7ddd..39e8bfa275d 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -836,6 +836,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, descriptorType = descriptor.nativeType elif isArgument: descriptorType = descriptor.argumentType + elif descriptor.interface.identifier.name == "WindowProxy": + conversionFunction = "windowproxy_from_handlevalue" if failureCode is None: substitutions = { @@ -2409,7 +2411,9 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config): 'crate::dom::bindings::conversions::ConversionBehavior', 'crate::dom::bindings::conversions::StringificationBehavior', 'crate::dom::bindings::conversions::root_from_handlevalue', + 'crate::dom::bindings::conversions::windowproxy_from_handlevalue', 'std::ptr::NonNull', + 'std::rc::Rc', 'crate::dom::bindings::record::Record', 'crate::dom::bindings::num::Finite', 'crate::dom::bindings::root::DomRoot', @@ -2419,10 +2423,12 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config): 'crate::dom::bindings::trace::RootedTraceableBox', 'crate::dom::bindings::utils::find_enum_value', 'crate::dom::types::*', + 'crate::dom::windowproxy::WindowProxy', 'crate::script_runtime::JSContext as SafeJSContext', 'js::error::throw_type_error', 'js::rust::HandleValue', 'js::jsapi::Heap', + 'js::jsapi::IsCallable', 'js::jsapi::JSContext', 'js::jsapi::JSObject', 'js::rust::MutableHandleValue', @@ -2438,9 +2444,10 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config): if not t.isUnion(): continue for memberType in t.flatMemberTypes: - if memberType.isDictionary() or memberType.isEnum(): + if memberType.isDictionary() or memberType.isEnum() or memberType.isCallback(): memberModule = getModuleFromObject(memberType) - memberName = memberType.inner.identifier.name + memberName = (memberType.callback.identifier.name + if memberType.isCallback() else memberType.inner.identifier.name) imports.append("%s::%s" % (memberModule, memberName)) if memberType.isEnum(): imports.append("%s::%sValues" % (memberModule, memberName)) @@ -4380,6 +4387,9 @@ def getUnionTypeTemplateVars(type, descriptorProvider): elif is_typed_array(type): name = type.name typeName = "typedarray::Heap" + name + elif type.isCallback(): + name = type.name + typeName = name else: raise TypeError("Can't handle %s in unions yet" % type) @@ -4418,12 +4428,19 @@ class CGUnionStruct(CGThing): return False def define(self): + def getTypeWrapper(t): + if type_needs_tracing(t): + return "RootedTraceableBox" + if t.isCallback(): + return "Rc" + return "" + templateVars = map(lambda t: (getUnionTypeTemplateVars(t, self.descriptorProvider), - type_needs_tracing(t)), + getTypeWrapper(t)), self.type.flatMemberTypes) enumValues = [ - " %s(%s)," % (v["name"], "RootedTraceableBox<%s>" % v["typeName"] if trace else v["typeName"]) - for (v, trace) in templateVars + " %s(%s)," % (v["name"], "%s<%s>" % (wrapper, v["typeName"]) if wrapper else v["typeName"]) + for (v, wrapper) in templateVars ] enumConversions = [ " %s::%s(ref inner) => inner.to_jsval(cx, rval)," @@ -4506,7 +4523,8 @@ class CGUnionConversionStruct(CGThing): callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes) if len(callbackMemberTypes) > 0: assert len(callbackMemberTypes) == 1 - raise TypeError("Can't handle callbacks in unions.") + typeName = callbackMemberTypes[0].name + callbackObject = CGGeneric(get_match(typeName)) else: callbackObject = None @@ -4537,7 +4555,7 @@ class CGUnionConversionStruct(CGThing): else: mozMapObject = None - hasObjectTypes = object or interfaceObject or arrayObject or dateObject or mozMapObject + hasObjectTypes = object or interfaceObject or arrayObject or dateObject 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) @@ -4548,6 +4566,8 @@ class CGUnionConversionStruct(CGThing): templateBody.append(interfaceObject) if arrayObject: templateBody.append(arrayObject) + if callbackObject: + templateBody.append(callbackObject) if mozMapObject: templateBody.append(mozMapObject) conversions.append(CGIfWrapper("value.get().is_object()", templateBody)) @@ -4608,6 +4628,8 @@ class CGUnionConversionStruct(CGThing): actualType = templateVars["typeName"] if type_needs_tracing(t): actualType = "RootedTraceableBox<%s>" % actualType + if t.isCallback(): + actualType = "Rc<%s>" % actualType returnType = "Result<Option<%s>, ()>" % actualType jsConversion = templateVars["jsConversion"] @@ -6388,8 +6410,11 @@ class CGDictionary(CGThing): def struct(self): d = self.dictionary if d.parent: - inheritance = " pub parent: %s::%s,\n" % (self.makeModuleName(d.parent), - self.makeClassName(d.parent)) + typeName = "%s::%s" % (self.makeModuleName(d.parent), + self.makeClassName(d.parent)) + if type_needs_tracing(d.parent): + typeName = "RootedTraceableBox<%s>" % typeName + inheritance = " pub parent: %s,\n" % typeName else: inheritance = "" memberDecls = [" pub %s: %s," % @@ -6399,8 +6424,9 @@ class CGDictionary(CGThing): derive = ["JSTraceable"] mustRoot = "" if self.membersNeedTracing(): - mustRoot = "#[must_root]\n" - derive += ["Default"] + mustRoot = "#[unrooted_must_root_lint::must_root]\n" + if not self.hasRequiredFields(self.dictionary): + derive += ["Default"] return (string.Template( "#[derive(${derive})]\n" @@ -6460,16 +6486,14 @@ class CGDictionary(CGThing): selfName = self.makeClassName(d) if self.membersNeedTracing(): actualType = "RootedTraceableBox<%s>" % selfName - preInitial = "let mut dictionary = RootedTraceableBox::new(%s::default());\n" % selfName - initParent = initParent = ("dictionary.parent = %s;\n" % initParent) if initParent else "" - memberInits = CGList([memberInit(m, False) for m in self.memberInfo]) - postInitial = "" + preInitial = "let dictionary = RootedTraceableBox::new(%s {\n" % selfName + postInitial = "});\n" else: actualType = selfName preInitial = "let dictionary = %s {\n" % selfName postInitial = "};\n" - initParent = ("parent: %s,\n" % initParent) if initParent else "" - memberInits = CGList([memberInit(m, True) for m in self.memberInfo]) + initParent = ("parent: %s,\n" % initParent) if initParent else "" + memberInits = CGList([memberInit(m, True) for m in self.memberInfo]) return string.Template( "impl ${selfName} {\n" @@ -6515,15 +6539,12 @@ class CGDictionary(CGThing): "initParent": CGIndenter(CGGeneric(initParent), indentLevel=16).define(), "initMembers": CGIndenter(memberInits, indentLevel=16).define(), "insertMembers": CGIndenter(memberInserts, indentLevel=8).define(), - "preInitial": CGIndenter(CGGeneric(preInitial), indentLevel=16).define(), - "postInitial": CGIndenter(CGGeneric(postInitial), indentLevel=16).define(), + "preInitial": CGIndenter(CGGeneric(preInitial), indentLevel=8).define(), + "postInitial": CGIndenter(CGGeneric(postInitial), indentLevel=8).define(), }) def membersNeedTracing(self): - for member, _ in self.memberInfo: - if type_needs_tracing(member.type): - return True - return False + return type_needs_tracing(self.dictionary) @staticmethod def makeDictionaryName(dictionary): @@ -6923,7 +6944,8 @@ class CGCallback(CGClass): bases=[ClassBase(baseName)], constructors=self.getConstructors(), methods=realMethods, - decorators="#[derive(JSTraceable, PartialEq)]\n#[allow_unrooted_interior]") + decorators="#[derive(JSTraceable, PartialEq)]\n" + "#[unrooted_must_root_lint::allow_unrooted_interior]") def getConstructors(self): return [ClassConstructor( @@ -7386,7 +7408,16 @@ class CGIterableMethodGenerator(CGGeneric): rooted!(in(*cx) let mut call_arg2 = UndefinedValue()); let mut call_args = vec![UndefinedValue(), UndefinedValue(), ObjectValue(*_obj)]; rooted!(in(*cx) let mut ignoredReturnVal = UndefinedValue()); - for i in 0..(*this).get_iterable_length() { + + // This has to be a while loop since get_iterable_length() may change during + // the callback, and we need to avoid iterator invalidation. + // + // It is possible for this to loop infinitely, but that matches the spec + // and other browsers. + // + // https://heycam.github.io/webidl/#es-forEach + let mut i = 0; + while i < (*this).get_iterable_length() { (*this).get_value_at_index(i).to_jsval(*cx, call_arg1.handle_mut()); (*this).get_key_at_index(i).to_jsval(*cx, call_arg2.handle_mut()); call_args[0] = call_arg1.handle().get(); @@ -7396,6 +7427,8 @@ class CGIterableMethodGenerator(CGGeneric): ignoredReturnVal.handle_mut()) { return false; } + + i += 1; } let result = (); diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs index c84adf64dbb..531ff6ea885 100644 --- a/components/script/dom/bindings/conversions.rs +++ b/components/script/dom/bindings/conversions.rs @@ -45,6 +45,7 @@ use crate::dom::htmlcollection::HTMLCollection; use crate::dom::htmlformcontrolscollection::HTMLFormControlsCollection; use crate::dom::htmloptionscollection::HTMLOptionsCollection; use crate::dom::nodelist::NodeList; +use crate::dom::windowproxy::WindowProxy; use js::conversions::latin1_to_string; pub use js::conversions::ConversionBehavior; pub use js::conversions::{ConversionResult, FromJSValConvertible, ToJSValConvertible}; @@ -55,10 +56,10 @@ use js::glue::{IsWrapper, UnwrapObjectDynamic}; use js::glue::{RUST_JSID_IS_INT, RUST_JSID_TO_INT}; use js::glue::{RUST_JSID_IS_STRING, RUST_JSID_TO_STRING}; use js::jsapi::{Heap, JSContext, JSObject, JSString}; +use js::jsapi::{IsWindowProxy, JS_NewStringCopyN, JS_StringHasLatin1Chars}; use js::jsapi::{ JS_GetLatin1StringCharsAndLength, JS_GetTwoByteStringCharsAndLength, JS_IsExceptionPending, }; -use js::jsapi::{JS_NewStringCopyN, JS_StringHasLatin1Chars}; use js::jsval::{ObjectValue, StringValue, UndefinedValue}; use js::rust::wrappers::{JS_GetProperty, JS_HasProperty, JS_IsArrayObject}; use js::rust::{get_object_class, is_dom_class, is_dom_object, maybe_wrap_value, ToString}; @@ -74,7 +75,6 @@ pub trait IDLInterface { } /// A trait to mark an IDL interface as deriving from another one. -#[rustc_on_unimplemented(message = "The IDL interface `{Self}` is not derived from `{T}`.")] pub trait DerivedFrom<T: Castable>: Castable {} impl<T: Float + ToJSValConvertible> ToJSValConvertible for Finite<T> { @@ -634,3 +634,22 @@ where Err(()) => Err(Error::JSFailed), } } + +/// Get a `DomRoot<T>` for a WindowProxy accessible from a `HandleValue`. +/// Caller is responsible for throwing a JS exception if needed in case of error. +pub unsafe fn windowproxy_from_handlevalue( + v: HandleValue, + _cx: *mut JSContext, +) -> Result<DomRoot<WindowProxy>, ()> { + if !v.get().is_object() { + return Err(()); + } + let object = v.get().to_object(); + if !IsWindowProxy(object) { + return Err(()); + } + let mut value = UndefinedValue(); + GetProxyReservedSlot(object, 0, &mut value); + let ptr = value.to_private() as *const WindowProxy; + Ok(DomRoot::from_ref(&*ptr)) +} diff --git a/components/script/dom/bindings/refcounted.rs b/components/script/dom/bindings/refcounted.rs index 8636f910121..ad5be5bb0cf 100644 --- a/components/script/dom/bindings/refcounted.rs +++ b/components/script/dom/bindings/refcounted.rs @@ -151,7 +151,7 @@ impl TrustedPromise { /// shared among threads for use in asynchronous operations. The underlying /// DOM object is guaranteed to live at least as long as the last outstanding /// `Trusted<T>` instance. -#[allow_unrooted_interior] +#[unrooted_must_root_lint::allow_unrooted_interior] pub struct Trusted<T: DomObject> { /// A pointer to the Rust DOM object of type T, but void to allow /// sending `Trusted<T>` between threads, regardless of T's sendability. diff --git a/components/script/dom/bindings/reflector.rs b/components/script/dom/bindings/reflector.rs index dc13fca77b0..2b5a4987eb5 100644 --- a/components/script/dom/bindings/reflector.rs +++ b/components/script/dom/bindings/reflector.rs @@ -30,7 +30,7 @@ where /// A struct to store a reference to the reflector of a DOM object. #[allow(unrooted_must_root)] #[derive(MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] // If you're renaming or moving this field, update the path in plugins::reflector as well pub struct Reflector { #[ignore_malloc_size_of = "defined and measured in rust-mozjs"] diff --git a/components/script/dom/bindings/root.rs b/components/script/dom/bindings/root.rs index 6e2c883984c..19df89df53f 100644 --- a/components/script/dom/bindings/root.rs +++ b/components/script/dom/bindings/root.rs @@ -46,7 +46,7 @@ use style::thread_state; /// A rooted value. #[allow(unrooted_must_root)] -#[allow_unrooted_interior] +#[unrooted_must_root_lint::allow_unrooted_interior] pub struct Root<T: StableTraceObject> { /// The value to root. value: T, @@ -283,7 +283,7 @@ where /// on the stack, the `Dom<T>` can point to freed memory. /// /// This should only be used as a field in other DOM objects. -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct Dom<T> { ptr: ptr::NonNull<T>, } @@ -343,7 +343,7 @@ unsafe impl<T: DomObject> JSTraceable for Dom<T> { /// An unrooted reference to a DOM object for use in layout. `Layout*Helpers` /// traits must be implemented on this. -#[allow_unrooted_interior] +#[unrooted_must_root_lint::allow_unrooted_interior] pub struct LayoutDom<T> { ptr: ptr::NonNull<T>, } @@ -463,7 +463,7 @@ impl LayoutDom<Node> { /// /// This should only be used as a field in other DOM objects; see warning /// on `Dom<T>`. -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(JSTraceable)] pub struct MutDom<T: DomObject> { val: UnsafeCell<Dom<T>>, @@ -518,7 +518,7 @@ impl<T: DomObject + PartialEq> PartialEq<T> for MutDom<T> { /// /// This should only be used as a field in other DOM objects; see warning /// on `Dom<T>`. -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(JSTraceable)] pub struct MutNullableDom<T: DomObject> { ptr: UnsafeCell<Option<Dom<T>>>, @@ -616,7 +616,7 @@ impl<T: DomObject> MallocSizeOf for MutNullableDom<T> { /// /// This should only be used as a field in other DOM objects; see warning /// on `Dom<T>`. -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct DomOnceCell<T: DomObject> { ptr: OnceCell<Dom<T>>, } diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 7869d1499ac..65717bb81bf 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -39,6 +39,7 @@ use crate::dom::bindings::utils::WindowProxyHandler; use crate::dom::document::PendingRestyle; use crate::dom::htmlimageelement::SourceSet; use crate::dom::htmlmediaelement::{HTMLMediaElementFetchContext, MediaFrameRenderer}; +use crate::dom::identityhub::Identities; use crate::task::TaskBox; use app_units::Au; use canvas_traits::canvas::{ @@ -57,7 +58,7 @@ use content_security_policy::CspList; use crossbeam_channel::{Receiver, Sender}; use cssparser::RGBA; use devtools_traits::{CSSError, TimelineMarkerType, WorkerId}; -use embedder_traits::EventLoopWaker; +use embedder_traits::{EventLoopWaker, MediaMetadata}; use encoding_rs::{Decoder, Encoding}; use euclid::default::{Point2D, Rect, Rotation3D, Transform2D, Transform3D}; use euclid::Length as EuclidLength; @@ -94,9 +95,9 @@ use profile_traits::time::ProfilerChan as TimeProfilerChan; use script_layout_interface::rpc::LayoutRPC; use script_layout_interface::OpaqueStyleAndLayoutData; use script_traits::transferable::MessagePortImpl; -use script_traits::DrawAPaintImageResult; -use script_traits::{DocumentActivity, ScriptToConstellationChan, TimerEventId, TimerSource}; -use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType}; +use script_traits::{DocumentActivity, DrawAPaintImageResult}; +use script_traits::{MediaSessionActionType, ScriptToConstellationChan, TimerEventId, TimerSource}; +use script_traits::{UntrustedNodeAddress, WebrenderIpcSender, WindowSizeData, WindowSizeType}; use selectors::matching::ElementSelectorFlags; use serde::{Deserialize, Serialize}; use servo_arc::Arc as ServoArc; @@ -107,7 +108,8 @@ use servo_media::audio::context::AudioContext; use servo_media::audio::graph::NodeId; use servo_media::audio::panner_node::{DistanceModel, PanningModel}; use servo_media::audio::param::ParamType; -use servo_media::player::frame::Frame; +use servo_media::player::audio::AudioRenderer; +use servo_media::player::video::VideoFrame; use servo_media::player::Player; use servo_media::streams::registry::MediaStreamId; use servo_media::streams::MediaStreamType; @@ -144,7 +146,8 @@ use tendril::stream::LossyDecoder; use tendril::{StrTendril, TendrilSink}; use time::{Duration, Timespec}; use uuid::Uuid; -use webrender_api::{DocumentId, ImageKey, RenderApiSender}; +use webgpu::{WebGPU, WebGPUAdapter}; +use webrender_api::{DocumentId, ImageKey}; use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState}; use webxr_api::SwapChainId as WebXRSwapChainId; @@ -501,6 +504,9 @@ unsafe_no_jsmanaged_fields!(WebGLTextureId); unsafe_no_jsmanaged_fields!(WebGLVertexArrayId); unsafe_no_jsmanaged_fields!(WebGLVersion); unsafe_no_jsmanaged_fields!(WebGLSLVersion); +unsafe_no_jsmanaged_fields!(WebGPU); +unsafe_no_jsmanaged_fields!(RefCell<Identities>); +unsafe_no_jsmanaged_fields!(WebGPUAdapter); unsafe_no_jsmanaged_fields!(WebXRSwapChainId); unsafe_no_jsmanaged_fields!(MediaList); unsafe_no_jsmanaged_fields!(WebVRGamepadData, WebVRGamepadState, WebVRGamepadHand); @@ -523,7 +529,6 @@ unsafe_no_jsmanaged_fields!(Arc<Mutex<dyn Player>>); unsafe_no_jsmanaged_fields!(WebRtcController); unsafe_no_jsmanaged_fields!(MediaStreamId, MediaStreamType); unsafe_no_jsmanaged_fields!(Mutex<MediaFrameRenderer>); -unsafe_no_jsmanaged_fields!(RenderApiSender); unsafe_no_jsmanaged_fields!(ResourceFetchTiming); unsafe_no_jsmanaged_fields!(Timespec); unsafe_no_jsmanaged_fields!(HTMLMediaElementFetchContext); @@ -532,8 +537,12 @@ unsafe_no_jsmanaged_fields!(Point2D<f32>, Rect<Au>); unsafe_no_jsmanaged_fields!(Rect<f32>); unsafe_no_jsmanaged_fields!(CascadeData); unsafe_no_jsmanaged_fields!(WindowGLContext); -unsafe_no_jsmanaged_fields!(Frame); +unsafe_no_jsmanaged_fields!(VideoFrame); unsafe_no_jsmanaged_fields!(WebGLContextId); +unsafe_no_jsmanaged_fields!(Arc<Mutex<dyn AudioRenderer>>); +unsafe_no_jsmanaged_fields!(MediaSessionActionType); +unsafe_no_jsmanaged_fields!(MediaMetadata); +unsafe_no_jsmanaged_fields!(WebrenderIpcSender); unsafe impl<'a> JSTraceable for &'a str { #[inline] @@ -883,7 +892,7 @@ impl<'a, T: JSTraceable + 'static> Drop for RootedTraceable<'a, T> { /// 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. -#[allow_unrooted_interior] +#[unrooted_must_root_lint::allow_unrooted_interior] pub struct RootedTraceableBox<T: 'static + JSTraceable> { ptr: *mut T, } @@ -954,7 +963,7 @@ impl<T: JSTraceable + 'static> Drop for RootedTraceableBox<T> { /// iterator of `DomRoot`s, `rooted_vec!(let v <- iterator);`. #[allow(unrooted_must_root)] #[derive(JSTraceable)] -#[allow_unrooted_interior] +#[unrooted_must_root_lint::allow_unrooted_interior] pub struct RootableVec<T: JSTraceable> { v: Vec<T>, } @@ -967,7 +976,7 @@ impl<T: JSTraceable> RootableVec<T> { } /// A vector of items that are rooted for the lifetime 'a. -#[allow_unrooted_interior] +#[unrooted_must_root_lint::allow_unrooted_interior] pub struct RootedVec<'a, T: 'static + JSTraceable> { root: &'a mut RootableVec<T>, } diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index ef3b270b130..e344e9ae279 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -16,11 +16,13 @@ use crate::dom::bindings::trace::trace_object; use crate::dom::messageport::MessagePort; use crate::dom::windowproxy; use crate::script_runtime::JSContext as SafeJSContext; -use js::conversions::ToJSValConvertible; +use js::conversions::{jsstr_to_string, ToJSValConvertible}; use js::glue::{CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper}; use js::glue::{GetCrossCompartmentWrapper, JS_GetReservedSlot, WrapperNew}; use js::glue::{UnwrapObjectDynamic, RUST_JSID_TO_INT, RUST_JSID_TO_STRING}; -use js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO, RUST_JSID_IS_INT, RUST_JSID_IS_STRING}; +use js::glue::{ + RUST_FUNCTION_VALUE_TO_JITINFO, RUST_JSID_IS_INT, RUST_JSID_IS_STRING, RUST_JSID_IS_VOID, +}; use js::jsapi::HandleId as RawHandleId; use js::jsapi::HandleObject as RawHandleObject; use js::jsapi::MutableHandleObject as RawMutableHandleObject; @@ -29,7 +31,10 @@ use js::jsapi::{Heap, JSAutoRealm, JSContext, JS_FreezeObject}; use js::jsapi::{JSJitInfo, JSObject, JSTracer, JSWrapObjectCallbacks}; use js::jsapi::{JS_EnumerateStandardClasses, JS_GetLatin1StringCharsAndLength}; use js::jsapi::{JS_IsExceptionPending, JS_IsGlobalObject}; -use js::jsapi::{JS_ResolveStandardClass, JS_StringHasLatin1Chars, ObjectOpResult}; +use js::jsapi::{ + JS_ResolveStandardClass, JS_StringHasLatin1Chars, ObjectOpResult, StringIsArrayIndex1, + StringIsArrayIndex2, +}; use js::jsval::{JSVal, UndefinedValue}; use js::rust::wrappers::JS_DeletePropertyById; use js::rust::wrappers::JS_ForwardGetPropertyTo; @@ -178,28 +183,41 @@ pub unsafe fn get_property_on_prototype( /// Get an array index from the given `jsid`. Returns `None` if the given /// `jsid` is not an integer. -pub fn get_array_index_from_id(_cx: *mut JSContext, id: HandleId) -> Option<u32> { +pub unsafe fn get_array_index_from_id(cx: *mut JSContext, id: HandleId) -> Option<u32> { let raw_id = id.into(); - unsafe { - if RUST_JSID_IS_INT(raw_id) { - return Some(RUST_JSID_TO_INT(raw_id) as u32); - } + if RUST_JSID_IS_INT(raw_id) { + return Some(RUST_JSID_TO_INT(raw_id) as u32); + } + + if RUST_JSID_IS_VOID(raw_id) || !RUST_JSID_IS_STRING(raw_id) { + return None; + } + + let s = jsstr_to_string(cx, RUST_JSID_TO_STRING(raw_id)); + if s.len() == 0 { + return None; + } + + let first = s.chars().next().unwrap(); + if first.is_ascii_lowercase() { + return None; + } + + let mut i: u32 = 0; + let is_array = if s.is_ascii() { + let chars = s.as_bytes(); + StringIsArrayIndex1(chars.as_ptr() as *const _, chars.len() as u32, &mut i) + } else { + let chars = s.encode_utf16().collect::<Vec<u16>>(); + let slice = chars.as_slice(); + StringIsArrayIndex2(slice.as_ptr(), chars.len() as u32, &mut i) + }; + + if is_array { + Some(i) + } else { None } - // if id is length atom, -1, otherwise - // return if JSID_IS_ATOM(id) { - // let atom = JSID_TO_ATOM(id); - // //let s = *GetAtomChars(id); - // if s > 'a' && s < 'z' { - // return -1; - // } - // - // let i = 0; - // let str = AtomToLinearString(JSID_TO_ATOM(id)); - // return if StringIsArray(str, &mut i) != 0 { i } else { -1 } - // } else { - // IdToInt32(cx, id); - // } } /// Find the enum equivelent of a string given by `v` in `pairs`. diff --git a/components/script/dom/bindings/weakref.rs b/components/script/dom/bindings/weakref.rs index bea2141d142..5c74e111816 100644 --- a/components/script/dom/bindings/weakref.rs +++ b/components/script/dom/bindings/weakref.rs @@ -34,13 +34,13 @@ pub const DOM_WEAK_SLOT: u32 = 1; /// A weak reference to a JS-managed DOM object. #[allow(unrooted_must_root)] -#[allow_unrooted_interior] +#[unrooted_must_root_lint::allow_unrooted_interior] pub struct WeakRef<T: WeakReferenceable> { ptr: ptr::NonNull<WeakBox<T>>, } /// The inner box of weak references, public for the finalization in codegen. -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct WeakBox<T: WeakReferenceable> { /// The reference count. When it reaches zero, the `value` field should /// have already been set to `None`. The pointee contributes one to the count. @@ -218,7 +218,7 @@ unsafe impl<T: WeakReferenceable> JSTraceable for MutableWeakRef<T> { /// A vector of weak references. On tracing, the vector retains /// only references which still point to live objects. -#[allow_unrooted_interior] +#[unrooted_must_root_lint::allow_unrooted_interior] #[derive(MallocSizeOf)] pub struct WeakRefVec<T: WeakReferenceable> { vec: Vec<WeakRef<T>>, @@ -268,7 +268,7 @@ impl<T: WeakReferenceable> DerefMut for WeakRefVec<T> { /// An entry of a vector of weak references. Passed to the closure /// given to `WeakRefVec::update`. -#[allow_unrooted_interior] +#[unrooted_must_root_lint::allow_unrooted_interior] pub struct WeakRefEntry<'a, T: WeakReferenceable> { vec: &'a mut WeakRefVec<T>, index: &'a mut usize, diff --git a/components/script/dom/blob.rs b/components/script/dom/blob.rs index fbf3e5819be..db90b2d2bda 100644 --- a/components/script/dom/blob.rs +++ b/components/script/dom/blob.rs @@ -31,7 +31,7 @@ pub struct FileBlob { } /// Different backends of Blob -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(JSTraceable)] pub enum BlobImpl { /// File-based blob, whose content lives in the net process diff --git a/components/script/dom/bluetooth.rs b/components/script/dom/bluetooth.rs index 989bcce27b7..fad367bb454 100644 --- a/components/script/dom/bluetooth.rs +++ b/components/script/dom/bluetooth.rs @@ -164,7 +164,7 @@ impl Bluetooth { &self, p: &Rc<Promise>, filters: &Option<Vec<BluetoothLEScanFilterInit>>, - optional_services: &Option<Vec<BluetoothServiceUUID>>, + optional_services: &[BluetoothServiceUUID], sender: IpcSender<BluetoothResponseResult>, ) { // TODO: Step 1: Triggered by user activation. @@ -197,23 +197,21 @@ impl Bluetooth { } let mut optional_services_uuids = vec![]; - if let &Some(ref opt_services) = optional_services { - for opt_service in opt_services { - // Step 2.5 - 2.6. - let uuid = match BluetoothUUID::service(opt_service.clone()) { - Ok(u) => u.to_string(), - Err(e) => { - p.reject_error(e); - return; - }, - }; + for opt_service in optional_services { + // Step 2.5 - 2.6. + let uuid = match BluetoothUUID::service(opt_service.clone()) { + Ok(u) => u.to_string(), + Err(e) => { + p.reject_error(e); + return; + }, + }; - // Step 2.7. - // Note: What we are doing here, is adding the not blocklisted UUIDs to the result vector, - // instead of removing them from an already filled vector. - if !uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { - optional_services_uuids.push(uuid); - } + // Step 2.7. + // Note: What we are doing here, is adding the not blocklisted UUIDs to the result vector, + // instead of removing them from an already filled vector. + if !uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { + optional_services_uuids.push(uuid); } } diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 9f6403bec5c..371b31bbdb0 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -22,9 +22,10 @@ use crate::dom::globalscope::GlobalScope; use crate::dom::htmlcanvaselement::HTMLCanvasElement; use crate::dom::imagedata::ImageData; use crate::dom::textmetrics::TextMetrics; +use crate::euclidext::Size2DExt; use canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg}; use dom_struct::dom_struct; -use euclid::default::{Rect, Size2D}; +use euclid::default::{Point2D, Rect, Size2D}; use ipc_channel::ipc::IpcSender; use servo_url::ServoUrl; use std::mem; @@ -75,12 +76,13 @@ impl CanvasRenderingContext2D { .borrow() .get_ipc_renderer() .send(CanvasMsg::Recreate( - size, + size.to_u64(), self.canvas_state.borrow().get_canvas_id(), )) .unwrap(); } + // TODO: This duplicates functionality in canvas state // https://html.spec.whatwg.org/multipage/#reset-the-rendering-context-to-its-default-state fn reset_to_initial_state(&self) { self.canvas_state @@ -90,6 +92,15 @@ impl CanvasRenderingContext2D { .clear(); *self.canvas_state.borrow().get_state().borrow_mut() = CanvasContextState::new(); } + /* + pub fn get_canvas_state(&self) -> Ref<CanvasState> { + self.canvas_state.borrow() + } + */ + + pub fn set_canvas_bitmap_dimensions(&self, size: Size2D<u64>) { + self.canvas_state.borrow().set_bitmap_dimensions(size); + } pub fn mark_as_dirty(&self) { self.canvas_state @@ -116,6 +127,7 @@ impl CanvasRenderingContext2D { self.canvas_state.borrow().send_canvas_2d_msg(msg) } + // TODO: Remove this pub fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg> { self.canvas_state.borrow().get_ipc_renderer().clone() } @@ -125,10 +137,14 @@ impl CanvasRenderingContext2D { } pub fn get_rect(&self, rect: Rect<u32>) -> Vec<u8> { + let rect = Rect::new( + Point2D::new(rect.origin.x as u64, rect.origin.y as u64), + Size2D::new(rect.size.width as u64, rect.size.height as u64), + ); self.canvas_state.borrow().get_rect( self.canvas .as_ref() - .map_or(Size2D::zero(), |c| c.get_size()), + .map_or(Size2D::zero(), |c| c.get_size().to_u64()), rect, ) } @@ -469,7 +485,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { self.canvas_state.borrow().get_image_data( self.canvas .as_ref() - .map_or(Size2D::zero(), |c| c.get_size()), + .map_or(Size2D::zero(), |c| c.get_size().to_u64()), &self.global(), sx, sy, @@ -483,7 +499,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { self.canvas_state.borrow().put_image_data( self.canvas .as_ref() - .map_or(Size2D::zero(), |c| c.get_size()), + .map_or(Size2D::zero(), |c| c.get_size().to_u64()), imagedata, dx, dy, @@ -505,7 +521,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { self.canvas_state.borrow().put_image_data_( self.canvas .as_ref() - .map_or(Size2D::zero(), |c| c.get_size()), + .map_or(Size2D::zero(), |c| c.get_size().to_u64()), imagedata, dx, dy, diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index d623f46005a..2ad565f9b45 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -39,7 +39,7 @@ pub struct CSSStyleDeclaration { } #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub enum CSSStyleOwner { Element(Dom<Element>), CSSRule( diff --git a/components/script/dom/customelementregistry.rs b/components/script/dom/customelementregistry.rs index 64117959500..7b4229835e2 100644 --- a/components/script/dom/customelementregistry.rs +++ b/components/script/dom/customelementregistry.rs @@ -706,7 +706,7 @@ pub fn try_upgrade_element(element: &Element) { } #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub enum CustomElementReaction { Upgrade(#[ignore_malloc_size_of = "Rc"] Rc<CustomElementDefinition>), Callback( @@ -752,7 +752,7 @@ enum BackupElementQueueFlag { /// <https://html.spec.whatwg.org/multipage/#custom-element-reactions-stack> #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct CustomElementReactionStack { stack: DomRefCell<Vec<ElementQueue>>, backup_queue: ElementQueue, @@ -934,7 +934,7 @@ impl CustomElementReactionStack { /// <https://html.spec.whatwg.org/multipage/#element-queue> #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] struct ElementQueue { queue: DomRefCell<VecDeque<Dom<Element>>>, } diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 3745ef9f1ac..2f339d1f8f8 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -32,11 +32,12 @@ use crate::script_runtime::{ new_child_runtime, CommonScriptMsg, JSContext as SafeJSContext, Runtime, ScriptChan, ScriptPort, }; use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue}; +use crate::task_source::networking::NetworkingTaskSource; use crate::task_source::TaskSourceName; use crossbeam_channel::{unbounded, Receiver, Sender}; use devtools_traits::DevtoolScriptControlMsg; use dom_struct::dom_struct; -use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use ipc_channel::ipc::IpcReceiver; use ipc_channel::router::ROUTER; use js::jsapi::JS_AddInterruptCallback; use js::jsapi::{Heap, JSContext, JSObject}; @@ -47,7 +48,7 @@ use net_traits::image_cache::ImageCache; use net_traits::request::{CredentialsMode, Destination, ParserMetadata}; use net_traits::request::{Referrer, RequestBuilder, RequestMode}; use net_traits::IpcSend; -use script_traits::{TimerEvent, TimerSource, WorkerGlobalScopeInit, WorkerScriptLoadOrigin}; +use script_traits::{WorkerGlobalScopeInit, WorkerScriptLoadOrigin}; use servo_rand::random; use servo_url::ServoUrl; use std::mem::replace; @@ -92,7 +93,6 @@ pub enum DedicatedWorkerScriptMsg { pub enum MixedMessage { FromWorker(DedicatedWorkerScriptMsg), - FromScheduler((TrustedWorkerAddress, TimerEvent)), FromDevtools(DevtoolScriptControlMsg), } @@ -173,8 +173,6 @@ pub struct DedicatedWorkerGlobalScope { task_queue: TaskQueue<DedicatedWorkerScriptMsg>, #[ignore_malloc_size_of = "Defined in std"] own_sender: Sender<DedicatedWorkerScriptMsg>, - #[ignore_malloc_size_of = "Defined in std"] - timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>, #[ignore_malloc_size_of = "Trusted<T> has unclear ownership like Dom<T>"] worker: DomRefCell<Option<TrustedWorkerAddress>>, #[ignore_malloc_size_of = "Can't measure trait objects"] @@ -185,14 +183,9 @@ pub struct DedicatedWorkerGlobalScope { } impl WorkerEventLoopMethods for DedicatedWorkerGlobalScope { - type TimerMsg = (TrustedWorkerAddress, TimerEvent); type WorkerMsg = DedicatedWorkerScriptMsg; type Event = MixedMessage; - fn timer_event_port(&self) -> &Receiver<(TrustedWorkerAddress, TimerEvent)> { - &self.timer_event_port - } - fn task_queue(&self) -> &TaskQueue<DedicatedWorkerScriptMsg> { &self.task_queue } @@ -210,10 +203,6 @@ impl WorkerEventLoopMethods for DedicatedWorkerGlobalScope { MixedMessage::FromWorker(msg) } - fn from_timer_msg(&self, msg: (TrustedWorkerAddress, TimerEvent)) -> MixedMessage { - MixedMessage::FromScheduler(msg) - } - fn from_devtools_msg(&self, msg: DevtoolScriptControlMsg) -> MixedMessage { MixedMessage::FromDevtools(msg) } @@ -230,8 +219,6 @@ impl DedicatedWorkerGlobalScope { parent_sender: Box<dyn ScriptChan + Send>, own_sender: Sender<DedicatedWorkerScriptMsg>, receiver: Receiver<DedicatedWorkerScriptMsg>, - timer_event_chan: IpcSender<TimerEvent>, - timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>, closing: Arc<AtomicBool>, image_cache: Arc<dyn ImageCache>, ) -> DedicatedWorkerGlobalScope { @@ -243,12 +230,10 @@ impl DedicatedWorkerGlobalScope { worker_url, runtime, from_devtools_receiver, - timer_event_chan, Some(closing), ), task_queue: TaskQueue::new(receiver, own_sender.clone()), own_sender: own_sender, - timer_event_port: timer_event_port, parent_sender: parent_sender, worker: DomRefCell::new(None), image_cache: image_cache, @@ -266,8 +251,6 @@ impl DedicatedWorkerGlobalScope { parent_sender: Box<dyn ScriptChan + Send>, own_sender: Sender<DedicatedWorkerScriptMsg>, receiver: Receiver<DedicatedWorkerScriptMsg>, - timer_event_chan: IpcSender<TimerEvent>, - timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>, closing: Arc<AtomicBool>, image_cache: Arc<dyn ImageCache>, ) -> DomRoot<DedicatedWorkerGlobalScope> { @@ -282,8 +265,6 @@ impl DedicatedWorkerGlobalScope { parent_sender, own_sender, receiver, - timer_event_chan, - timer_event_port, closing, image_cache, )); @@ -344,7 +325,20 @@ impl DedicatedWorkerGlobalScope { .referrer_policy(referrer_policy) .origin(origin); - let runtime = unsafe { new_child_runtime(parent) }; + let runtime = unsafe { + if let Some(pipeline_id) = pipeline_id { + let task_source = NetworkingTaskSource( + Box::new(WorkerThreadWorkerChan { + sender: own_sender.clone(), + worker: worker.clone(), + }), + pipeline_id, + ); + new_child_runtime(parent, Some(task_source)) + } else { + new_child_runtime(parent, None) + } + }; let (devtools_mpsc_chan, devtools_mpsc_port) = unbounded(); ROUTER.route_ipc_receiver_to_crossbeam_sender( @@ -352,17 +346,6 @@ impl DedicatedWorkerGlobalScope { devtools_mpsc_chan, ); - let (timer_tx, timer_rx) = unbounded(); - let (timer_ipc_chan, timer_ipc_port) = ipc::channel().unwrap(); - let worker_for_route = worker.clone(); - ROUTER.add_route( - timer_ipc_port.to_opaque(), - Box::new(move |message| { - let event = message.to().unwrap(); - timer_tx.send((worker_for_route.clone(), event)).unwrap(); - }), - ); - let global = DedicatedWorkerGlobalScope::new( init, DOMString::from_string(worker_name), @@ -373,8 +356,6 @@ impl DedicatedWorkerGlobalScope { parent_sender.clone(), own_sender, receiver, - timer_ipc_chan, - timer_rx, closing, image_cache, ); @@ -503,14 +484,6 @@ impl DedicatedWorkerGlobalScope { }, _ => debug!("got an unusable devtools control message inside the worker!"), }, - MixedMessage::FromScheduler((linked_worker, timer_event)) => match timer_event { - TimerEvent(TimerSource::FromWorker, id) => { - let _ar = AutoWorkerReset::new(self, linked_worker); - let scope = self.upcast::<WorkerGlobalScope>(); - scope.handle_fire_timer(id); - }, - TimerEvent(_, _) => panic!("A worker received a TimerEvent from a window."), - }, MixedMessage::FromWorker(DedicatedWorkerScriptMsg::CommonWorker( linked_worker, msg, @@ -619,8 +592,6 @@ impl DedicatedWorkerGlobalScopeMethods for DedicatedWorkerGlobalScope { let mut rooted = CustomAutoRooter::new( options .transfer - .as_ref() - .unwrap_or(&Vec::with_capacity(0)) .iter() .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get()) .collect(), diff --git a/components/script/dom/dissimilaroriginwindow.rs b/components/script/dom/dissimilaroriginwindow.rs index c628923604e..ac866a90ff8 100644 --- a/components/script/dom/dissimilaroriginwindow.rs +++ b/components/script/dom/dissimilaroriginwindow.rs @@ -15,7 +15,6 @@ use crate::dom::globalscope::GlobalScope; use crate::dom::windowproxy::WindowProxy; use crate::script_runtime::JSContext; use dom_struct::dom_struct; -use ipc_channel::ipc; use js::jsapi::{Heap, JSObject}; use js::jsval::{JSVal, UndefinedValue}; use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue}; @@ -48,8 +47,6 @@ impl DissimilarOriginWindow { #[allow(unsafe_code)] pub fn new(global_to_clone_from: &GlobalScope, window_proxy: &WindowProxy) -> DomRoot<Self> { let cx = global_to_clone_from.get_cx(); - // Any timer events fired on this window are ignored. - let (timer_event_chan, _) = ipc::channel().unwrap(); let win = Box::new(Self { globalscope: GlobalScope::new_inherited( PipelineId::new(), @@ -59,7 +56,6 @@ impl DissimilarOriginWindow { global_to_clone_from.script_to_constellation_chan().clone(), global_to_clone_from.scheduler_chan().clone(), global_to_clone_from.resource_threads().clone(), - timer_event_chan, global_to_clone_from.origin().clone(), // FIXME(nox): The microtask queue is probably not important // here, but this whole DOM interface is a hack anyway. @@ -141,15 +137,9 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow { cx: JSContext, message: HandleValue, target_origin: USVString, - mut transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>, + transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>, ) -> ErrorResult { - if transfer.is_some() { - let mut rooted = CustomAutoRooter::new(transfer.take().unwrap()); - let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted)); - self.post_message_impl(&target_origin, cx, message, transfer) - } else { - self.post_message_impl(&target_origin, cx, message, None) - } + self.post_message_impl(&target_origin, cx, message, transfer) } /// https://html.spec.whatwg.org/multipage/#dom-window-postmessage-options @@ -161,14 +151,13 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow { ) -> ErrorResult { let mut rooted = CustomAutoRooter::new( options + .parent .transfer - .as_ref() - .unwrap_or(&Vec::with_capacity(0)) .iter() .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get()) .collect(), ); - let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted)); + let transfer = CustomAutoRooterGuard::new(*cx, &mut rooted); self.post_message_impl(&options.targetOrigin, cx, message, transfer) } @@ -208,10 +197,10 @@ impl DissimilarOriginWindow { target_origin: &USVString, cx: JSContext, message: HandleValue, - transfer: Option<CustomAutoRooterGuard<Vec<*mut JSObject>>>, + transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>, ) -> ErrorResult { // Step 6-7. - let data = structuredclone::write(cx, message, transfer)?; + let data = structuredclone::write(cx, message, Some(transfer))?; self.post_message(target_origin, data) } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 0efb0856098..a7a56665bf1 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -506,62 +506,69 @@ impl Document { pub fn set_activity(&self, activity: DocumentActivity) { // This function should only be called on documents with a browsing context assert!(self.has_browsing_context); + if activity == self.activity.get() { + return; + } + // Set the document's activity level, reflow if necessary, and suspend or resume timers. - if activity != self.activity.get() { - self.activity.set(activity); - let media = ServoMedia::get().unwrap(); - let pipeline_id = self.window().pipeline_id().expect("doc with no pipeline"); - let client_context_id = - ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get()); - if activity == DocumentActivity::FullyActive { - self.title_changed(); - self.dirty_all_nodes(); - self.window() - .reflow(ReflowGoal::Full, ReflowReason::CachedPageNeededReflow); - self.window().resume(); - media.resume(&client_context_id); - // html.spec.whatwg.org/multipage/#history-traversal - // Step 4.6 - if self.ready_state.get() == DocumentReadyState::Complete { - let document = Trusted::new(self); - self.window - .task_manager() - .dom_manipulation_task_source() - .queue( - task!(fire_pageshow_event: move || { - let document = document.root(); - let window = document.window(); - // Step 4.6.1 - if document.page_showing.get() { - return; - } - // Step 4.6.2 - document.page_showing.set(true); - // Step 4.6.4 - let event = PageTransitionEvent::new( - window, - atom!("pageshow"), - false, // bubbles - false, // cancelable - true, // persisted - ); - let event = event.upcast::<Event>(); - event.set_trusted(true); - // FIXME(nox): Why are errors silenced here? - let _ = window.upcast::<EventTarget>().dispatch_event_with_target( - document.upcast(), - &event, - ); - }), - self.window.upcast(), - ) - .unwrap(); - } - } else { - self.window().suspend(); - media.suspend(&client_context_id); - } + self.activity.set(activity); + let media = ServoMedia::get().unwrap(); + let pipeline_id = self.window().pipeline_id().expect("doc with no pipeline"); + let client_context_id = + ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get()); + + if activity != DocumentActivity::FullyActive { + self.window().suspend(); + media.suspend(&client_context_id); + return; } + + self.title_changed(); + self.dirty_all_nodes(); + self.window() + .reflow(ReflowGoal::Full, ReflowReason::CachedPageNeededReflow); + self.window().resume(); + media.resume(&client_context_id); + + if self.ready_state.get() != DocumentReadyState::Complete { + return; + } + + // html.spec.whatwg.org/multipage/#history-traversal + // Step 4.6 + let document = Trusted::new(self); + self.window + .task_manager() + .dom_manipulation_task_source() + .queue( + task!(fire_pageshow_event: move || { + let document = document.root(); + let window = document.window(); + // Step 4.6.1 + if document.page_showing.get() { + return; + } + // Step 4.6.2 + document.page_showing.set(true); + // Step 4.6.4 + let event = PageTransitionEvent::new( + window, + atom!("pageshow"), + false, // bubbles + false, // cancelable + true, // persisted + ); + let event = event.upcast::<Event>(); + event.set_trusted(true); + // FIXME(nox): Why are errors silenced here? + let _ = window.upcast::<EventTarget>().dispatch_event_with_target( + document.upcast(), + &event, + ); + }), + self.window.upcast(), + ) + .unwrap(); } pub fn origin(&self) -> &MutableOrigin { @@ -1436,7 +1443,8 @@ impl Document { // https://w3c.github.io/uievents/#keys-cancelable-keys if keyboard_event.state == KeyState::Down && - keyboard_event.key.legacy_charcode() != 0 && + is_character_value_key(&(keyboard_event.key)) && + !keyboard_event.is_composing && cancel_state != EventDefault::Prevented { // https://w3c.github.io/uievents/#keypress-event-order @@ -2517,6 +2525,13 @@ impl Document { } } +fn is_character_value_key(key: &Key) -> bool { + match key { + Key::Character(_) | Key::Enter => true, + _ => false, + } +} + #[derive(MallocSizeOf, PartialEq)] pub enum DocumentSource { FromParser, @@ -4741,7 +4756,7 @@ impl AnimationFrameCallback { } #[derive(Default, JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] struct PendingInOrderScriptVec { scripts: DomRefCell<VecDeque<PendingScript>>, } @@ -4779,7 +4794,7 @@ impl PendingInOrderScriptVec { } #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] struct PendingScript { element: Dom<HTMLScriptElement>, load: Option<ScriptResult>, diff --git a/components/script/dom/documentorshadowroot.rs b/components/script/dom/documentorshadowroot.rs index 594801584c8..724ea11da8c 100644 --- a/components/script/dom/documentorshadowroot.rs +++ b/components/script/dom/documentorshadowroot.rs @@ -28,7 +28,7 @@ use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuar use style::stylesheets::{CssRule, Origin, Stylesheet}; #[derive(Clone, JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct StyleSheetInDocument { #[ignore_malloc_size_of = "Arc"] pub sheet: Arc<Stylesheet>, @@ -76,7 +76,7 @@ impl ::style::stylesheets::StylesheetInDocument for StyleSheetInDocument { } // https://w3c.github.io/webcomponents/spec/shadow/#extensions-to-the-documentorshadowroot-mixin -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(JSTraceable, MallocSizeOf)] pub struct DocumentOrShadowRoot { window: Dom<Window>, diff --git a/components/script/dom/formdataevent.rs b/components/script/dom/formdataevent.rs index f7ebb57bbfb..c380abf217a 100644 --- a/components/script/dom/formdataevent.rs +++ b/components/script/dom/formdataevent.rs @@ -5,7 +5,7 @@ use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods; use crate::dom::bindings::codegen::Bindings::FormDataEventBinding; use crate::dom::bindings::codegen::Bindings::FormDataEventBinding::FormDataEventMethods; -use crate::dom::bindings::error::{Error, Fallible}; +use crate::dom::bindings::error::Fallible; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::{Dom, DomRoot}; @@ -56,21 +56,12 @@ impl FormDataEvent { let bubbles = EventBubbles::from(init.parent.bubbles); let cancelable = EventCancelable::from(init.parent.cancelable); - let form_data = match init.formData { - Some(ref form_data) => form_data.clone(), - None => { - return Err(Error::Type( - "required member formData is undefined".to_string(), - )); - }, - }; - let event = FormDataEvent::new( &window.global(), Atom::from(type_), bubbles, cancelable, - &*form_data, + &*init.formData.clone(), ); Ok(event) diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 7d07e67f9ec..4896c59e048 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -39,6 +39,7 @@ use crate::task_source::networking::NetworkingTaskSource; use crate::task_source::performance_timeline::PerformanceTimelineTaskSource; use crate::task_source::port_message::PortMessageQueue; use crate::task_source::remote_event::RemoteEventTaskSource; +use crate::task_source::timer::TimerTaskSource; use crate::task_source::websocket::WebsocketTaskSource; use crate::task_source::TaskSource; use crate::task_source::TaskSourceName; @@ -134,8 +135,13 @@ pub struct GlobalScope { /// including resource_thread, filemanager_thread and storage_thread resource_threads: ResourceThreads, + /// The mechanism by which time-outs and intervals are scheduled. + /// <https://html.spec.whatwg.org/multipage/#timers> timers: OneshotTimers, + /// Have timers been initialized? + init_timers: Cell<bool>, + /// The origin of the globalscope origin: MutableOrigin, @@ -188,6 +194,13 @@ struct MessageListener { context: Trusted<GlobalScope>, } +/// A wrapper between timer events coming in over IPC, and the event-loop. +struct TimerListener { + canceller: TaskCanceller, + task_source: TimerTaskSource, + context: Trusted<GlobalScope>, +} + /// Data representing a message-port managed by this global. #[derive(JSTraceable, MallocSizeOf)] pub enum ManagedMessagePort { @@ -212,18 +225,82 @@ pub enum MessagePortState { UnManaged, } +impl TimerListener { + /// Handle a timer-event coming-in over IPC, + /// by queuing the appropriate task on the relevant event-loop. + fn handle(&self, event: TimerEvent) { + let context = self.context.clone(); + // Step 18, queue a task, + // https://html.spec.whatwg.org/multipage/#timer-initialisation-steps + let _ = self.task_source.queue_with_canceller( + task!(timer_event: move || { + let global = context.root(); + let TimerEvent(source, id) = event; + match source { + TimerSource::FromWorker => { + global.downcast::<WorkerGlobalScope>().expect("Window timer delivered to worker"); + }, + TimerSource::FromWindow(pipeline) => { + assert_eq!(pipeline, global.pipeline_id()); + global.downcast::<Window>().expect("Worker timer delivered to window"); + }, + }; + // Step 7, substeps run in a task. + global.fire_timer(id); + }), + &self.canceller, + ); + } +} + impl MessageListener { /// A new message came in, handle it via a task enqueued on the event-loop. /// A task is required, since we are using a trusted globalscope, /// and we can only access the root from the event-loop. fn notify(&self, msg: MessagePortMsg) { match msg { - MessagePortMsg::CompleteTransfer(port_id, tasks) => { + MessagePortMsg::CompleteTransfer(ports) => { let context = self.context.clone(); let _ = self.task_source.queue_with_canceller( task!(process_complete_transfer: move || { let global = context.root(); - global.complete_port_transfer(port_id, tasks); + + let router_id = match global.port_router_id() { + Some(router_id) => router_id, + None => { + // If not managing any ports, no transfer can succeed, + // so just send back everything. + let _ = global.script_to_constellation_chan().send( + ScriptMsg::MessagePortTransferResult(None, vec![], ports), + ); + return; + } + }; + + let mut succeeded = vec![]; + let mut failed = HashMap::new(); + + for (id, buffer) in ports.into_iter() { + if global.is_managing_port(&id) { + succeeded.push(id.clone()); + global.complete_port_transfer(id, buffer); + } else { + failed.insert(id, buffer); + } + } + let _ = global.script_to_constellation_chan().send( + ScriptMsg::MessagePortTransferResult(Some(router_id), succeeded, failed), + ); + }), + &self.canceller, + ); + }, + MessagePortMsg::CompletePendingTransfer(port_id, buffer) => { + let context = self.context.clone(); + let _ = self.task_source.queue_with_canceller( + task!(complete_pending: move || { + let global = context.root(); + global.complete_port_transfer(port_id, buffer); }), &self.canceller, ); @@ -261,7 +338,6 @@ impl GlobalScope { script_to_constellation_chan: ScriptToConstellationChan, scheduler_chan: IpcSender<TimerSchedulerMsg>, resource_threads: ResourceThreads, - timer_event_chan: IpcSender<TimerEvent>, origin: MutableOrigin, microtask_queue: Rc<MicrotaskQueue>, is_headless: bool, @@ -282,7 +358,8 @@ impl GlobalScope { scheduler_chan: scheduler_chan.clone(), in_error_reporting_mode: Default::default(), resource_threads, - timers: OneshotTimers::new(timer_event_chan, scheduler_chan), + timers: OneshotTimers::new(scheduler_chan), + init_timers: Default::default(), origin, microtask_queue, list_auto_close_worker: Default::default(), @@ -294,6 +371,55 @@ impl GlobalScope { } } + /// The message-port router Id of the global, if any + fn port_router_id(&self) -> Option<MessagePortRouterId> { + if let MessagePortState::Managed(id, _message_ports) = &*self.message_port_state.borrow() { + Some(id.clone()) + } else { + None + } + } + + /// Is this global managing a given port? + fn is_managing_port(&self, port_id: &MessagePortId) -> bool { + if let MessagePortState::Managed(_router_id, message_ports) = + &*self.message_port_state.borrow() + { + return message_ports.contains_key(port_id); + } + false + } + + /// Setup the IPC-to-event-loop glue for timers to schedule themselves. + fn setup_timers(&self) { + if self.init_timers.get() { + return; + } + self.init_timers.set(true); + + let (timer_ipc_chan, timer_ipc_port) = ipc::channel().unwrap(); + self.timers.setup_scheduling(timer_ipc_chan); + + // Setup route from IPC to task-queue for the timer-task-source. + let context = Trusted::new(&*self); + let (task_source, canceller) = ( + self.timer_task_source(), + self.task_canceller(TaskSourceName::Timer), + ); + let timer_listener = TimerListener { + context, + task_source, + canceller, + }; + ROUTER.add_route( + timer_ipc_port.to_opaque(), + Box::new(move |message| { + let event = message.to().unwrap(); + timer_listener.handle(event); + }), + ); + } + /// Complete the transfer of a message-port. fn complete_port_transfer(&self, port_id: MessagePortId, tasks: VecDeque<PortMessageTask>) { let should_start = if let MessagePortState::Managed(_id, message_ports) = @@ -301,7 +427,7 @@ impl GlobalScope { { match message_ports.get_mut(&port_id) { None => { - panic!("CompleteTransfer msg received in a global not managing the port."); + panic!("complete_port_transfer called for an unknown port."); }, Some(ManagedMessagePort::Pending(_, _)) => { panic!("CompleteTransfer msg received for a pending port."); @@ -312,7 +438,7 @@ impl GlobalScope { }, } } else { - return warn!("CompleteTransfer msg received in a global not managing any ports."); + panic!("complete_port_transfer called for an unknown port."); }; if should_start { self.start_message_port(&port_id); @@ -554,22 +680,25 @@ impl GlobalScope { _ => None, }) .collect(); - for id in to_be_added { + for id in to_be_added.iter() { let (id, port_info) = message_ports .remove_entry(&id) .expect("Collected port-id to match an entry"); - if let ManagedMessagePort::Pending(port_impl, dom_port) = port_info { - let _ = self - .script_to_constellation_chan() - .send(ScriptMsg::NewMessagePort( - router_id.clone(), - port_impl.message_port_id().clone(), - )); - let new_port_info = ManagedMessagePort::Added(port_impl, dom_port); - let present = message_ports.insert(id, new_port_info); - assert!(present.is_none()); + match port_info { + ManagedMessagePort::Pending(port_impl, dom_port) => { + let new_port_info = ManagedMessagePort::Added(port_impl, dom_port); + let present = message_ports.insert(id, new_port_info); + assert!(present.is_none()); + }, + _ => panic!("Only pending ports should be found in to_be_added"), } } + let _ = + self.script_to_constellation_chan() + .send(ScriptMsg::CompleteMessagePortTransfer( + router_id.clone(), + to_be_added, + )); } else { warn!("maybe_add_pending_ports called on a global not managing any ports."); } @@ -1005,6 +1134,18 @@ impl GlobalScope { unreachable!(); } + /// `TaskSource` to send messages to the timer queue of + /// this global scope. + pub fn timer_task_source(&self) -> TimerTaskSource { + if let Some(window) = self.downcast::<Window>() { + return window.task_manager().timer_task_source(); + } + if let Some(worker) = self.downcast::<WorkerGlobalScope>() { + return worker.timer_task_source(); + } + unreachable!(); + } + /// `TaskSource` to send messages to the remote-event task source of /// this global scope. pub fn remote_event_task_source(&self) -> RemoteEventTaskSource { @@ -1087,11 +1228,13 @@ impl GlobalScope { ) } + /// <https://html.spec.whatwg.org/multipage/#timer-initialisation-steps> pub fn schedule_callback( &self, callback: OneshotTimerCallback, duration: MsDuration, ) -> OneshotTimerHandle { + self.setup_timers(); self.timers .schedule_callback(callback, duration, self.timer_source()) } @@ -1100,6 +1243,7 @@ impl GlobalScope { self.timers.unschedule_callback(handle); } + /// <https://html.spec.whatwg.org/multipage/#timer-initialisation-steps> pub fn set_timeout_or_interval( &self, callback: TimerCallback, @@ -1107,6 +1251,7 @@ impl GlobalScope { timeout: i32, is_interval: IsInterval, ) -> i32 { + self.setup_timers(); self.timers.set_timeout_or_interval( self, callback, @@ -1118,27 +1263,27 @@ impl GlobalScope { } pub fn clear_timeout_or_interval(&self, handle: i32) { - self.timers.clear_timeout_or_interval(self, handle) + self.timers.clear_timeout_or_interval(self, handle); } pub fn fire_timer(&self, handle: TimerEventId) { - self.timers.fire_timer(handle, self) + self.timers.fire_timer(handle, self); } pub fn resume(&self) { - self.timers.resume() + self.timers.resume(); } pub fn suspend(&self) { - self.timers.suspend() + self.timers.suspend(); } pub fn slow_down_timers(&self) { - self.timers.slow_down() + self.timers.slow_down(); } pub fn speed_up_timers(&self) { - self.timers.speed_up() + self.timers.speed_up(); } fn timer_source(&self) -> TimerSource { diff --git a/components/script/dom/gpu.rs b/components/script/dom/gpu.rs new file mode 100644 index 00000000000..4e47620052e --- /dev/null +++ b/components/script/dom/gpu.rs @@ -0,0 +1,161 @@ +/* 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::compartments::InCompartment; +use crate::dom::bindings::codegen::Bindings::GPUBinding::GPURequestAdapterOptions; +use crate::dom::bindings::codegen::Bindings::GPUBinding::{self, GPUMethods, GPUPowerPreference}; +use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; +use crate::dom::bindings::error::Error; +use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; +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::gpuadapter::GPUAdapter; +use crate::dom::promise::Promise; +use crate::task_source::TaskSource; +use dom_struct::dom_struct; +use ipc_channel::ipc::{self, IpcSender}; +use ipc_channel::router::ROUTER; +use js::jsapi::Heap; +use std::rc::Rc; +use webgpu::wgpu; +use webgpu::{WebGPU, WebGPURequest, WebGPUResponse, WebGPUResponseResult}; + +#[dom_struct] +pub struct GPU { + reflector_: Reflector, +} + +impl GPU { + pub fn new_inherited() -> GPU { + GPU { + reflector_: Reflector::new(), + } + } + + pub fn new(global: &GlobalScope) -> DomRoot<GPU> { + reflect_dom_object(Box::new(GPU::new_inherited()), global, GPUBinding::Wrap) + } + + fn wgpu_channel(&self) -> Option<WebGPU> { + self.global().as_window().webgpu_channel() + } +} + +pub trait AsyncWGPUListener { + fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>); +} + +struct WGPUResponse<T: AsyncWGPUListener + DomObject> { + trusted: TrustedPromise, + receiver: Trusted<T>, +} + +impl<T: AsyncWGPUListener + DomObject> WGPUResponse<T> { + #[allow(unrooted_must_root)] + fn response(self, response: WebGPUResponseResult) { + let promise = self.trusted.root(); + match response { + Ok(response) => self.receiver.root().handle_response(response, &promise), + Err(error) => promise.reject_error(Error::Type(format!( + "Received error from WebGPU thread: {}", + error + ))), + } + } +} + +pub fn response_async<T: AsyncWGPUListener + DomObject + 'static>( + promise: &Rc<Promise>, + receiver: &T, +) -> IpcSender<WebGPUResponseResult> { + let (action_sender, action_receiver) = ipc::channel().unwrap(); + let (task_source, canceller) = receiver + .global() + .as_window() + .task_manager() + .dom_manipulation_task_source_with_canceller(); + let mut trusted = Some(TrustedPromise::new(promise.clone())); + let trusted_receiver = Trusted::new(receiver); + ROUTER.add_route( + action_receiver.to_opaque(), + Box::new(move |message| { + let trusted = if let Some(trusted) = trusted.take() { + trusted + } else { + error!("WebGPU callback called twice!"); + return; + }; + + let context = WGPUResponse { + trusted, + receiver: trusted_receiver.clone(), + }; + let result = task_source.queue_with_canceller( + task!(process_webgpu_task: move|| { + context.response(message.to().unwrap()); + }), + &canceller, + ); + if let Err(err) = result { + error!("Failed to queue GPU listener-task: {:?}", err); + } + }), + ); + action_sender +} + +impl GPUMethods for GPU { + // https://gpuweb.github.io/gpuweb/#dom-gpu-requestadapter + fn RequestAdapter( + &self, + options: &GPURequestAdapterOptions, + comp: InCompartment, + ) -> Rc<Promise> { + let promise = Promise::new_in_current_compartment(&self.global(), comp); + let sender = response_async(&promise, self); + let power_preference = match options.powerPreference { + Some(GPUPowerPreference::Low_power) => wgpu::PowerPreference::LowPower, + Some(GPUPowerPreference::High_performance) => wgpu::PowerPreference::HighPerformance, + None => wgpu::PowerPreference::Default, + }; + let id = self.global().as_window().Navigator().create_adapter_id(); + + match self.wgpu_channel() { + Some(channel) => { + channel + .0 + .send(WebGPURequest::RequestAdapter( + sender, + wgpu::RequestAdapterOptions { power_preference }, + id, + )) + .unwrap(); + }, + None => promise.reject_error(Error::Type("No WebGPU thread...".to_owned())), + }; + promise + } +} + +impl AsyncWGPUListener for GPU { + fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>) { + match response { + WebGPUResponse::RequestAdapter(name, adapter) => { + let adapter = GPUAdapter::new( + &self.global(), + DOMString::from(name), + Heap::default(), + adapter, + ); + promise.resolve_native(&adapter); + }, + response => promise.reject_error(Error::Type(format!( + "Wrong response received for GPU from WebGPU thread {:?}", + response, + ))), + } + } +} diff --git a/components/script/dom/gpuadapter.rs b/components/script/dom/gpuadapter.rs new file mode 100644 index 00000000000..ec970439e8a --- /dev/null +++ b/components/script/dom/gpuadapter.rs @@ -0,0 +1,63 @@ +/* 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::codegen::Bindings::GPUAdapterBinding::{self, GPUAdapterMethods}; +use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::globalscope::GlobalScope; +use crate::script_runtime::JSContext as SafeJSContext; +use dom_struct::dom_struct; +use js::jsapi::{Heap, JSObject}; +use std::ptr::NonNull; +use webgpu::WebGPUAdapter; + +#[dom_struct] +pub struct GPUAdapter { + reflector_: Reflector, + name: DOMString, + #[ignore_malloc_size_of = "mozjs"] + extensions: Heap<*mut JSObject>, + adapter: WebGPUAdapter, +} + +impl GPUAdapter { + pub fn new_inherited( + name: DOMString, + extensions: Heap<*mut JSObject>, + adapter: WebGPUAdapter, + ) -> GPUAdapter { + GPUAdapter { + reflector_: Reflector::new(), + name, + extensions, + adapter, + } + } + + pub fn new( + global: &GlobalScope, + name: DOMString, + extensions: Heap<*mut JSObject>, + adapter: WebGPUAdapter, + ) -> DomRoot<GPUAdapter> { + reflect_dom_object( + Box::new(GPUAdapter::new_inherited(name, extensions, adapter)), + global, + GPUAdapterBinding::Wrap, + ) + } +} + +impl GPUAdapterMethods for GPUAdapter { + // https://gpuweb.github.io/gpuweb/#dom-gpuadapter-name + fn Name(&self) -> DOMString { + self.name.clone() + } + + // https://gpuweb.github.io/gpuweb/#dom-gpuadapter-extensions + fn Extensions(&self, _cx: SafeJSContext) -> NonNull<JSObject> { + NonNull::new(self.extensions.get()).unwrap() + } +} diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index 9e638539995..89410a6b6b0 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -28,6 +28,7 @@ use crate::dom::webgl2renderingcontext::WebGL2RenderingContext; use crate::dom::webglrenderingcontext::{ LayoutCanvasWebGLRenderingContextHelpers, WebGLRenderingContext, }; +use crate::euclidext::Size2DExt; use crate::script_runtime::JSContext; use base64; use canvas_traits::canvas::{CanvasId, CanvasMsg, FromScriptMsg}; @@ -49,7 +50,7 @@ use style::attr::{AttrValue, LengthOrPercentageOrAuto}; const DEFAULT_WIDTH: u32 = 300; const DEFAULT_HEIGHT: u32 = 150; -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(Clone, JSTraceable, MallocSizeOf)] pub enum CanvasContext { Context2d(Dom<CanvasRenderingContext2D>), @@ -94,7 +95,9 @@ impl HTMLCanvasElement { let size = self.get_size(); if let Some(ref context) = *self.context.borrow() { match *context { - CanvasContext::Context2d(ref context) => context.set_bitmap_dimensions(size), + CanvasContext::Context2d(ref context) => { + context.set_canvas_bitmap_dimensions(size.to_u64()) + }, CanvasContext::WebGL(ref context) => context.recreate(size), CanvasContext::WebGL2(ref context) => context.recreate(size), } diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index f5a820458c2..b70663a6949 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -28,7 +28,6 @@ use crate::dom::windowproxy::WindowProxy; use crate::script_thread::ScriptThread; use crate::task_source::TaskSource; use dom_struct::dom_struct; -use euclid::Size2D; use html5ever::{LocalName, Prefix}; use ipc_channel::ipc; use msg::constellation_msg::{BrowsingContextId, PipelineId, TopLevelBrowsingContextId}; @@ -131,10 +130,12 @@ impl HTMLIFrameElement { let document = document_from_node(self); - let mut load_blocker = self.load_blocker.borrow_mut(); - // Any oustanding load is finished from the point of view of the blocked - // document; the new navigation will continue blocking it. - LoadBlocker::terminate(&mut load_blocker); + { + let mut load_blocker = self.load_blocker.borrow_mut(); + // Any oustanding load is finished from the point of view of the blocked + // document; the new navigation will continue blocking it. + LoadBlocker::terminate(&mut load_blocker); + } if load_data.url.scheme() == "javascript" { let window_proxy = self.GetContentWindow(); @@ -151,6 +152,7 @@ impl HTMLIFrameElement { match load_data.js_eval_result { Some(JsEvalResult::NoContent) => (), _ => { + let mut load_blocker = self.load_blocker.borrow_mut(); *load_blocker = Some(LoadBlocker::new( &*document, LoadType::Subframe(load_data.url.clone()), @@ -173,6 +175,13 @@ impl HTMLIFrameElement { replace: replace, }; + let window_size = WindowSizeData { + initial_viewport: window + .inner_window_dimensions_query(browsing_context_id) + .unwrap_or_default(), + device_pixel_ratio: window.device_pixel_ratio(), + }; + match nav_type { NavigationType::InitialAboutBlank => { let (pipeline_sender, pipeline_receiver) = ipc::channel().unwrap(); @@ -184,6 +193,7 @@ impl HTMLIFrameElement { load_data: load_data.clone(), old_pipeline_id: old_pipeline_id, sandbox: sandboxed, + window_size, }; global_scope .script_to_constellation_chan() @@ -198,13 +208,7 @@ impl HTMLIFrameElement { opener: None, load_data: load_data, pipeline_port: pipeline_receiver, - window_size: WindowSizeData { - initial_viewport: { - let rect = self.upcast::<Node>().bounding_content_box_or_zero(); - Size2D::new(rect.size.width.to_f32_px(), rect.size.height.to_f32_px()) - }, - device_pixel_ratio: window.device_pixel_ratio(), - }, + window_size, }; self.pipeline_id.set(Some(new_pipeline_id)); @@ -216,6 +220,7 @@ impl HTMLIFrameElement { load_data: load_data, old_pipeline_id: old_pipeline_id, sandbox: sandboxed, + window_size, }; global_scope .script_to_constellation_chan() @@ -227,7 +232,30 @@ impl HTMLIFrameElement { /// <https://html.spec.whatwg.org/multipage/#process-the-iframe-attributes> fn process_the_iframe_attributes(&self, mode: ProcessingMode) { - // TODO: srcdoc + if self + .upcast::<Element>() + .has_attribute(&local_name!("srcdoc")) + { + let url = ServoUrl::parse("about:srcdoc").unwrap(); + let document = document_from_node(self); + let window = window_from_node(self); + let pipeline_id = Some(window.upcast::<GlobalScope>().pipeline_id()); + let mut load_data = LoadData::new( + LoadOrigin::Script(document.origin().immutable().clone()), + url, + pipeline_id, + Some(Referrer::ReferrerUrl(document.url())), + document.get_referrer_policy(), + ); + let element = self.upcast::<Element>(); + load_data.srcdoc = String::from(element.get_string_attribute(&local_name!("srcdoc"))); + self.navigate_or_reload_child_browsing_context( + load_data, + NavigationType::InitialAboutBlank, + HistoryEntryReplacement::Disabled, + ); + return; + } let window = window_from_node(self); @@ -478,6 +506,12 @@ impl HTMLIFrameElementMethods for HTMLIFrameElement { // https://html.spec.whatwg.org/multipage/#dom-iframe-src make_url_setter!(SetSrc, "src"); + // https://html.spec.whatwg.org/multipage/#dom-iframe-srcdoc + make_getter!(Srcdoc, "srcdoc"); + + // https://html.spec.whatwg.org/multipage/#dom-iframe-srcdoc + make_setter!(SetSrcdoc, "srcdoc"); + // https://html.spec.whatwg.org/multipage/#dom-iframe-sandbox fn Sandbox(&self) -> DomRoot<DOMTokenList> { self.sandbox @@ -578,13 +612,29 @@ impl VirtualMethods for HTMLIFrameElement { modes })); }, + &local_name!("srcdoc") => { + // https://html.spec.whatwg.org/multipage/#the-iframe-element:the-iframe-element-9 + // "Whenever an iframe element with a non-null nested browsing context has its + // srcdoc attribute set, changed, or removed, the user agent must process the + // iframe attributes." + // but we can't check that directly, since the child browsing context + // may be in a different script thread. Instead, we check to see if the parent + // is in a document tree and has a browsing context, which is what causes + // the child browsing context to be created. + + // trigger the processing of iframe attributes whenever "srcdoc" attribute is set, changed or removed + if self.upcast::<Node>().is_connected_with_browsing_context() { + debug!("iframe srcdoc modified while in browsing context."); + self.process_the_iframe_attributes(ProcessingMode::NotFirstTime); + } + }, &local_name!("src") => { // https://html.spec.whatwg.org/multipage/#the-iframe-element // "Similarly, whenever an iframe element with a non-null nested browsing context // but with no srcdoc attribute specified has its src attribute set, changed, or removed, // the user agent must process the iframe attributes," // but we can't check that directly, since the child browsing context - // may be in a different script thread. Instread, we check to see if the parent + // may be in a different script thread. Instead, we check to see if the parent // is in a document tree and has a browsing context, which is what causes // the child browsing context to be created. if self.upcast::<Node>().is_connected_with_browsing_context() { diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 525f35d3e95..5b6bd321534 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -133,7 +133,7 @@ enum ImageRequestPhase { Current, } #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] struct ImageRequest { state: State, parsed_url: Option<ServoUrl>, @@ -927,10 +927,7 @@ impl HTMLImageElement { *self.last_selected_source.borrow_mut() = selected_source.clone(); // Step 6, check the list of available images - if !selected_source - .as_ref() - .map_or(false, |source| source.is_empty()) - { + if let Some(src) = selected_source { if let Ok(img_url) = base_url.join(&src) { let image_cache = window.image_cache(); let response = image_cache.find_image_or_metadata( diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 60b5cb9bcb1..5fdbad5c1bd 100755 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -237,7 +237,7 @@ pub struct HTMLInputElement { } #[derive(JSTraceable)] -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(MallocSizeOf)] struct InputActivationState { indeterminate: bool, diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 13c5cd126c7..651084cfb84 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -15,8 +15,10 @@ use crate::dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaE use crate::dom::bindings::codegen::Bindings::HTMLSourceElementBinding::HTMLSourceElementMethods; use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConstants::*; use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods; +use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorBinding::NavigatorMethods; use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeBinding::NodeMethods; use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{TextTrackKind, TextTrackMode}; +use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use crate::dom::bindings::codegen::InheritTypes::{ElementTypeId, HTMLElementTypeId}; use crate::dom::bindings::codegen::InheritTypes::{HTMLMediaElementTypeId, NodeTypeId}; use crate::dom::bindings::codegen::UnionTypes::{ @@ -65,6 +67,7 @@ use crate::script_thread::ScriptThread; use crate::task_source::TaskSource; use dom_struct::dom_struct; use embedder_traits::resources::{self, Resource as EmbedderResource}; +use embedder_traits::{MediaSessionEvent, MediaSessionPlaybackState}; use euclid::default::Size2D; use headers::{ContentLength, ContentRange, HeaderMapExt}; use html5ever::{LocalName, Prefix}; @@ -78,8 +81,10 @@ use net_traits::request::{Destination, Referrer}; use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, Metadata}; use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType}; use script_layout_interface::HTMLMediaData; +use script_traits::WebrenderIpcSender; use servo_config::pref; -use servo_media::player::frame::{Frame, FrameRenderer}; +use servo_media::player::audio::AudioRenderer; +use servo_media::player::video::{VideoFrame, VideoFrameRenderer}; use servo_media::player::{PlaybackState, Player, PlayerError, PlayerEvent, SeekLock, StreamType}; use servo_media::{ClientContextId, ServoMedia, SupportsMediaType}; use servo_url::ServoUrl; @@ -91,8 +96,7 @@ 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, RenderApi}; -use webrender_api::{RenderApiSender, Transaction}; +use webrender_api::{ImageData, ImageDescriptor, ImageFormat, ImageKey, Transaction}; #[derive(PartialEq)] enum FrameStatus { @@ -100,10 +104,10 @@ enum FrameStatus { Unlocked, } -struct FrameHolder(FrameStatus, Frame); +struct FrameHolder(FrameStatus, VideoFrame); impl FrameHolder { - fn new(frame: Frame) -> FrameHolder { + fn new(frame: VideoFrame) -> FrameHolder { FrameHolder(FrameStatus::Unlocked, frame) } @@ -119,7 +123,7 @@ impl FrameHolder { }; } - fn set(&mut self, new_frame: Frame) { + fn set(&mut self, new_frame: VideoFrame) { if self.0 == FrameStatus::Unlocked { self.1 = new_frame }; @@ -137,14 +141,14 @@ impl FrameHolder { } } - fn get_frame(&self) -> Frame { + fn get_frame(&self) -> VideoFrame { self.1.clone() } } pub struct MediaFrameRenderer { player_id: Option<u64>, - api: RenderApi, + api: WebrenderIpcSender, current_frame: Option<(ImageKey, i32, i32)>, old_frame: Option<ImageKey>, very_old_frame: Option<ImageKey>, @@ -152,10 +156,10 @@ pub struct MediaFrameRenderer { } impl MediaFrameRenderer { - fn new(render_api_sender: RenderApiSender) -> Self { + fn new(render_api_sender: WebrenderIpcSender) -> Self { Self { player_id: None, - api: render_api_sender.create_api(), + api: render_api_sender, current_frame: None, old_frame: None, very_old_frame: None, @@ -170,8 +174,8 @@ impl MediaFrameRenderer { } } -impl FrameRenderer for MediaFrameRenderer { - fn render(&mut self, frame: Frame) { +impl VideoFrameRenderer for MediaFrameRenderer { + fn render(&mut self, frame: VideoFrame) { let mut txn = Transaction::new(); if let Some(old_image_key) = mem::replace(&mut self.very_old_frame, self.old_frame.take()) { @@ -268,7 +272,7 @@ impl FrameRenderer for MediaFrameRenderer { } } -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(JSTraceable, MallocSizeOf)] enum SrcObject { MediaStream(Dom<MediaStream>), @@ -325,7 +329,9 @@ pub struct HTMLMediaElement { #[ignore_malloc_size_of = "servo_media"] player: DomRefCell<Option<Arc<Mutex<dyn Player>>>>, #[ignore_malloc_size_of = "Arc"] - frame_renderer: Arc<Mutex<MediaFrameRenderer>>, + video_renderer: Arc<Mutex<MediaFrameRenderer>>, + #[ignore_malloc_size_of = "Arc"] + audio_renderer: DomRefCell<Option<Arc<Mutex<dyn AudioRenderer>>>>, /// https://html.spec.whatwg.org/multipage/#show-poster-flag show_poster: Cell<bool>, /// https://html.spec.whatwg.org/multipage/#dom-media-duration @@ -410,9 +416,10 @@ impl HTMLMediaElement { pending_play_promises: Default::default(), in_flight_play_promises_queue: Default::default(), player: Default::default(), - frame_renderer: Arc::new(Mutex::new(MediaFrameRenderer::new( + video_renderer: Arc::new(Mutex::new(MediaFrameRenderer::new( document.window().get_webrender_api_sender(), ))), + audio_renderer: Default::default(), show_poster: Cell::new(true), duration: Cell::new(f64::NAN), playback_position: Cell::new(0.), @@ -588,7 +595,6 @@ impl HTMLMediaElement { match (old_ready_state, ready_state) { (ReadyState::HaveNothing, ReadyState::HaveMetadata) => { task_source.queue_simple_event(self.upcast(), atom!("loadedmetadata"), &window); - // No other steps are applicable in this case. return; }, @@ -1293,7 +1299,7 @@ impl HTMLMediaElement { // Step 6. if let ImageResponse::Loaded(image, _) = image { - self.frame_renderer + self.video_renderer .lock() .unwrap() .render_poster_frame(image); @@ -1325,11 +1331,14 @@ impl HTMLMediaElement { let window = window_from_node(self); let (action_sender, action_receiver) = ipc::channel::<PlayerEvent>().unwrap(); - let renderer: Option<Arc<Mutex<dyn FrameRenderer>>> = match self.media_type_id() { + let video_renderer: Option<Arc<Mutex<dyn VideoFrameRenderer>>> = match self.media_type_id() + { HTMLMediaElementTypeId::HTMLAudioElement => None, - HTMLMediaElementTypeId::HTMLVideoElement => Some(self.frame_renderer.clone()), + HTMLMediaElementTypeId::HTMLVideoElement => Some(self.video_renderer.clone()), }; + 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"); @@ -1339,7 +1348,8 @@ impl HTMLMediaElement { &client_context_id, stream_type, action_sender, - renderer, + video_renderer, + audio_renderer, Box::new(window.get_player_context()), ); @@ -1385,7 +1395,7 @@ impl HTMLMediaElement { .unwrap_or((0, None)); self.id.set(player_id); - self.frame_renderer.lock().unwrap().player_id = Some(player_id); + self.video_renderer.lock().unwrap().player_id = Some(player_id); if let Some(image_receiver) = image_receiver { let trusted_node = Trusted::new(self); @@ -1400,11 +1410,11 @@ impl HTMLMediaElement { if let Err(err) = task_source.queue_with_canceller( task!(handle_glplayer_message: move || { trace!("GLPlayer message {:?}", msg); - let frame_renderer = this.root().frame_renderer.clone(); + let video_renderer = this.root().video_renderer.clone(); match msg { GLPlayerMsgForward::Lock(sender) => { - frame_renderer + video_renderer .lock() .unwrap() .current_frame_holder @@ -1415,7 +1425,7 @@ impl HTMLMediaElement { }); }, GLPlayerMsgForward::Unlock() => { - frame_renderer + video_renderer .lock() .unwrap() .current_frame_holder @@ -1527,7 +1537,7 @@ impl HTMLMediaElement { ))); self.upcast::<EventTarget>().fire_event(atom!("error")); }, - PlayerEvent::FrameUpdated => { + PlayerEvent::VideoFrameUpdated => { self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage); }, PlayerEvent::MetadataUpdated(ref metadata) => { @@ -1717,6 +1727,17 @@ impl HTMLMediaElement { if self.Controls() { self.render_controls(); } + + let global = self.global(); + let window = global.as_window(); + + // Update the media session metadata title with the obtained metadata. + window.Navigator().MediaSession().update_title( + metadata + .title + .clone() + .unwrap_or(window.get_url().into_string()), + ); }, PlayerEvent::NeedData => { // The player needs more data. @@ -1774,13 +1795,33 @@ impl HTMLMediaElement { }; ScriptThread::await_stable_state(Microtask::MediaElement(task)); }, - PlayerEvent::StateChanged(ref state) => match *state { - PlaybackState::Paused => { - if self.ready_state.get() == ReadyState::HaveMetadata { - self.change_ready_state(ReadyState::HaveEnoughData); - } - }, - _ => {}, + PlayerEvent::StateChanged(ref state) => { + let mut media_session_playback_state = MediaSessionPlaybackState::None_; + match *state { + PlaybackState::Paused => { + media_session_playback_state = MediaSessionPlaybackState::Paused; + if self.ready_state.get() == ReadyState::HaveMetadata { + self.change_ready_state(ReadyState::HaveEnoughData); + } + }, + PlaybackState::Playing => { + media_session_playback_state = MediaSessionPlaybackState::Playing; + }, + PlaybackState::Buffering => { + // Do not send the media session playback state change event + // in this case as a None_ state is expected to clean up the + // session. + return; + }, + _ => {}, + }; + debug!( + "Sending media session event playback state changed to {:?}", + media_session_playback_state + ); + self.send_media_session_event(MediaSessionEvent::PlaybackStateChange( + media_session_playback_state, + )); }, } } @@ -1855,12 +1896,35 @@ impl HTMLMediaElement { } } - pub fn get_current_frame(&self) -> Option<Frame> { - match self.frame_renderer.lock().unwrap().current_frame_holder { + pub fn get_current_frame(&self) -> Option<VideoFrame> { + match self.video_renderer.lock().unwrap().current_frame_holder { Some(ref holder) => Some(holder.get_frame()), None => return None, } } + + /// By default the audio is rendered through the audio sink automatically + /// selected by the servo-media Player instance. However, in some cases, like + /// the WebAudio MediaElementAudioSourceNode, we need to set a custom audio + /// renderer. + pub fn set_audio_renderer(&self, audio_renderer: Arc<Mutex<dyn AudioRenderer>>) { + *self.audio_renderer.borrow_mut() = Some(audio_renderer); + if let Some(ref player) = *self.player.borrow() { + if let Err(e) = player.lock().unwrap().stop() { + eprintln!("Could not stop player {:?}", e); + } + self.media_element_load_algorithm(); + } + } + + fn send_media_session_event(&self, event: MediaSessionEvent) { + let global = self.global(); + let media_session = global.as_window().Navigator().MediaSession(); + + media_session.register_media_instance(&self); + + media_session.send_event(event); + } } // XXX Placeholder for [https://github.com/servo/servo/issues/22293] @@ -2365,7 +2429,7 @@ impl LayoutHTMLMediaElementHelpers for LayoutDom<HTMLMediaElement> { fn data(&self) -> HTMLMediaData { let media = unsafe { &*self.unsafe_get() }; HTMLMediaData { - current_frame: media.frame_renderer.lock().unwrap().current_frame.clone(), + current_frame: media.video_renderer.lock().unwrap().current_frame.clone(), } } } diff --git a/components/script/dom/htmlvideoelement.rs b/components/script/dom/htmlvideoelement.rs index 55990c1ffe5..69f8b9a85e6 100644 --- a/components/script/dom/htmlvideoelement.rs +++ b/components/script/dom/htmlvideoelement.rs @@ -35,7 +35,7 @@ use net_traits::{ CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, FetchResponseMsg, }; use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType}; -use servo_media::player::frame::Frame; +use servo_media::player::video::VideoFrame; use servo_url::ServoUrl; use std::cell::Cell; use std::sync::{Arc, Mutex}; @@ -58,8 +58,8 @@ pub struct HTMLVideoElement { /// is being fetched. load_blocker: DomRefCell<Option<LoadBlocker>>, /// A copy of the last frame - #[ignore_malloc_size_of = "Frame"] - last_frame: DomRefCell<Option<Frame>>, + #[ignore_malloc_size_of = "VideoFrame"] + last_frame: DomRefCell<Option<VideoFrame>>, } impl HTMLVideoElement { diff --git a/components/script/dom/identityhub.rs b/components/script/dom/identityhub.rs new file mode 100644 index 00000000000..489eaacc78d --- /dev/null +++ b/components/script/dom/identityhub.rs @@ -0,0 +1,47 @@ +/* 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 webgpu::wgpu::{AdapterId, Backend, DeviceId, IdentityManager, SurfaceId}; + +#[derive(Debug)] +pub struct IdentityHub { + adapters: IdentityManager<AdapterId>, + devices: IdentityManager<DeviceId>, +} + +impl IdentityHub { + fn new(backend: Backend) -> Self { + IdentityHub { + adapters: IdentityManager::new(backend), + devices: IdentityManager::new(backend), + } + } +} + +#[derive(Debug)] +pub struct Identities { + surface: IdentityManager<SurfaceId>, + hub: IdentityHub, +} + +impl Identities { + pub fn new() -> Self { + let hub = if cfg!(any(target_os = "linux", target_os = "windows")) { + IdentityHub::new(Backend::Vulkan) + } else if cfg!(any(target_os = "ios", target_os = "macos")) { + IdentityHub::new(Backend::Metal) + } else { + IdentityHub::new(Backend::Empty) + }; + + Identities { + surface: IdentityManager::new(Backend::Empty), + hub, + } + } + + pub fn create_adapter_id(&mut self) -> AdapterId { + self.hub.adapters.alloc() + } +} diff --git a/components/script/dom/imagedata.rs b/components/script/dom/imagedata.rs index d89e5e36d1b..b09984e267f 100644 --- a/components/script/dom/imagedata.rs +++ b/components/script/dom/imagedata.rs @@ -169,8 +169,8 @@ impl ImageData { } #[allow(unsafe_code)] - pub unsafe fn get_rect(&self, rect: Rect<u32>) -> Cow<[u8]> { - pixels::rgba8_get_rect(self.as_slice(), self.get_size(), rect) + pub unsafe fn get_rect(&self, rect: Rect<u64>) -> Cow<[u8]> { + pixels::rgba8_get_rect(self.as_slice(), self.get_size().to_u64(), rect) } pub fn get_size(&self) -> Size2D<u32> { @@ -194,3 +194,13 @@ impl ImageDataMethods for ImageData { NonNull::new(self.data.get()).expect("got a null pointer") } } + +pub trait Size2DExt { + fn to_u64(&self) -> Size2D<u64>; +} + +impl Size2DExt for Size2D<u32> { + fn to_u64(&self) -> Size2D<u64> { + return Size2D::new(self.width as u64, self.height as u64); + } +} diff --git a/components/script/dom/mediaelementaudiosourcenode.rs b/components/script/dom/mediaelementaudiosourcenode.rs new file mode 100644 index 00000000000..07ca70bd80e --- /dev/null +++ b/components/script/dom/mediaelementaudiosourcenode.rs @@ -0,0 +1,80 @@ +/* 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::audiocontext::AudioContext; +use crate::dom::audionode::AudioNode; +use crate::dom::bindings::codegen::Bindings::MediaElementAudioSourceNodeBinding; +use crate::dom::bindings::codegen::Bindings::MediaElementAudioSourceNodeBinding::MediaElementAudioSourceNodeMethods; +use crate::dom::bindings::codegen::Bindings::MediaElementAudioSourceNodeBinding::MediaElementAudioSourceOptions; +use crate::dom::bindings::error::Fallible; +use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::htmlmediaelement::HTMLMediaElement; +use crate::dom::window::Window; +use dom_struct::dom_struct; +use servo_media::audio::media_element_source_node::MediaElementSourceNodeMessage; +use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage}; +use std::sync::mpsc; + +#[dom_struct] +pub struct MediaElementAudioSourceNode { + node: AudioNode, + media_element: Dom<HTMLMediaElement>, +} + +impl MediaElementAudioSourceNode { + #[allow(unrooted_must_root)] + fn new_inherited( + context: &AudioContext, + media_element: &HTMLMediaElement, + ) -> Fallible<MediaElementAudioSourceNode> { + let node = AudioNode::new_inherited( + AudioNodeInit::MediaElementSourceNode, + &*context.base(), + Default::default(), + 0, + 1, + )?; + let (sender, receiver) = mpsc::channel(); + node.message(AudioNodeMessage::MediaElementSourceNode( + MediaElementSourceNodeMessage::GetAudioRenderer(sender), + )); + let audio_renderer = receiver.recv().unwrap(); + media_element.set_audio_renderer(audio_renderer); + let media_element = Dom::from_ref(media_element); + Ok(MediaElementAudioSourceNode { + node, + media_element, + }) + } + + #[allow(unrooted_must_root)] + pub fn new( + window: &Window, + context: &AudioContext, + media_element: &HTMLMediaElement, + ) -> Fallible<DomRoot<MediaElementAudioSourceNode>> { + let node = MediaElementAudioSourceNode::new_inherited(context, media_element)?; + Ok(reflect_dom_object( + Box::new(node), + window, + MediaElementAudioSourceNodeBinding::Wrap, + )) + } + + pub fn Constructor( + window: &Window, + context: &AudioContext, + options: &MediaElementAudioSourceOptions, + ) -> Fallible<DomRoot<MediaElementAudioSourceNode>> { + MediaElementAudioSourceNode::new(window, context, &*options.mediaElement) + } +} + +impl MediaElementAudioSourceNodeMethods for MediaElementAudioSourceNode { + /// https://webaudio.github.io/web-audio-api/#dom-mediaelementaudiosourcenode-mediaelement + fn MediaElement(&self) -> DomRoot<HTMLMediaElement> { + DomRoot::from_ref(&*self.media_element) + } +} diff --git a/components/script/dom/mediametadata.rs b/components/script/dom/mediametadata.rs new file mode 100644 index 00000000000..f2e94abfaa1 --- /dev/null +++ b/components/script/dom/mediametadata.rs @@ -0,0 +1,97 @@ +/* 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::MediaMetadataBinding; +use crate::dom::bindings::codegen::Bindings::MediaMetadataBinding::MediaMetadataInit; +use crate::dom::bindings::codegen::Bindings::MediaMetadataBinding::MediaMetadataMethods; +use crate::dom::bindings::error::Fallible; +use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::{DomRoot, MutNullableDom}; +use crate::dom::bindings::str::DOMString; +use crate::dom::mediasession::MediaSession; +use crate::dom::window::Window; +use dom_struct::dom_struct; + +#[dom_struct] +pub struct MediaMetadata { + reflector_: Reflector, + session: MutNullableDom<MediaSession>, + title: DomRefCell<DOMString>, + artist: DomRefCell<DOMString>, + album: DomRefCell<DOMString>, +} + +impl MediaMetadata { + fn new_inherited(init: &MediaMetadataInit) -> MediaMetadata { + MediaMetadata { + reflector_: Reflector::new(), + session: Default::default(), + title: DomRefCell::new(init.title.clone()), + artist: DomRefCell::new(init.artist.clone()), + album: DomRefCell::new(init.album.clone()), + } + } + + pub fn new(global: &Window, init: &MediaMetadataInit) -> DomRoot<MediaMetadata> { + reflect_dom_object( + Box::new(MediaMetadata::new_inherited(init)), + global, + MediaMetadataBinding::Wrap, + ) + } + + /// https://w3c.github.io/mediasession/#dom-mediametadata-mediametadata + pub fn Constructor( + window: &Window, + init: &MediaMetadataInit, + ) -> Fallible<DomRoot<MediaMetadata>> { + Ok(MediaMetadata::new(window, init)) + } + + fn queue_update_metadata_algorithm(&self) { + if self.session.get().is_none() { + return; + } + } + + pub fn set_session(&self, session: &MediaSession) { + self.session.set(Some(&session)); + } +} + +impl MediaMetadataMethods for MediaMetadata { + /// https://w3c.github.io/mediasession/#dom-mediametadata-title + fn Title(&self) -> DOMString { + self.title.borrow().clone() + } + + /// https://w3c.github.io/mediasession/#dom-mediametadata-title + fn SetTitle(&self, value: DOMString) { + *self.title.borrow_mut() = value; + self.queue_update_metadata_algorithm(); + } + + /// https://w3c.github.io/mediasession/#dom-mediametadata-artist + fn Artist(&self) -> DOMString { + self.artist.borrow().clone() + } + + /// https://w3c.github.io/mediasession/#dom-mediametadata-artist + fn SetArtist(&self, value: DOMString) { + *self.artist.borrow_mut() = value; + self.queue_update_metadata_algorithm(); + } + + /// https://w3c.github.io/mediasession/#dom-mediametadata-album + fn Album(&self) -> DOMString { + self.album.borrow().clone() + } + + /// https://w3c.github.io/mediasession/#dom-mediametadata-album + fn SetAlbum(&self, value: DOMString) { + *self.album.borrow_mut() = value; + self.queue_update_metadata_algorithm(); + } +} diff --git a/components/script/dom/mediasession.rs b/components/script/dom/mediasession.rs new file mode 100644 index 00000000000..1523e9a0ae6 --- /dev/null +++ b/components/script/dom/mediasession.rs @@ -0,0 +1,213 @@ +/* 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::compartments::{AlreadyInCompartment, InCompartment}; +use crate::dom::bindings::callback::ExceptionHandling; +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaElementMethods; +use crate::dom::bindings::codegen::Bindings::MediaMetadataBinding::MediaMetadataInit; +use crate::dom::bindings::codegen::Bindings::MediaMetadataBinding::MediaMetadataMethods; +use crate::dom::bindings::codegen::Bindings::MediaSessionBinding; +use crate::dom::bindings::codegen::Bindings::MediaSessionBinding::MediaSessionAction; +use crate::dom::bindings::codegen::Bindings::MediaSessionBinding::MediaSessionActionHandler; +use crate::dom::bindings::codegen::Bindings::MediaSessionBinding::MediaSessionMethods; +use crate::dom::bindings::codegen::Bindings::MediaSessionBinding::MediaSessionPlaybackState; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; +use crate::dom::bindings::root::{DomRoot, MutNullableDom}; +use crate::dom::bindings::str::DOMString; +use crate::dom::htmlmediaelement::HTMLMediaElement; +use crate::dom::mediametadata::MediaMetadata; +use crate::dom::window::Window; +use dom_struct::dom_struct; +use embedder_traits::MediaMetadata as EmbedderMediaMetadata; +use embedder_traits::MediaSessionEvent; +use script_traits::MediaSessionActionType; +use script_traits::ScriptMsg; +use std::collections::HashMap; +use std::rc::Rc; + +#[dom_struct] +pub struct MediaSession { + reflector_: Reflector, + /// https://w3c.github.io/mediasession/#dom-mediasession-metadata + #[ignore_malloc_size_of = "defined in embedder_traits"] + metadata: DomRefCell<Option<EmbedderMediaMetadata>>, + /// https://w3c.github.io/mediasession/#dom-mediasession-playbackstate + playback_state: DomRefCell<MediaSessionPlaybackState>, + /// https://w3c.github.io/mediasession/#supported-media-session-actions + #[ignore_malloc_size_of = "Rc"] + action_handlers: DomRefCell<HashMap<MediaSessionActionType, Rc<MediaSessionActionHandler>>>, + /// The media instance controlled by this media session. + /// For now only HTMLMediaElements are controlled by media sessions. + media_instance: MutNullableDom<HTMLMediaElement>, +} + +impl MediaSession { + #[allow(unrooted_must_root)] + fn new_inherited() -> MediaSession { + let media_session = MediaSession { + reflector_: Reflector::new(), + metadata: DomRefCell::new(None), + playback_state: DomRefCell::new(MediaSessionPlaybackState::None), + action_handlers: DomRefCell::new(HashMap::new()), + media_instance: Default::default(), + }; + media_session + } + + pub fn new(window: &Window) -> DomRoot<MediaSession> { + reflect_dom_object( + Box::new(MediaSession::new_inherited()), + window, + MediaSessionBinding::Wrap, + ) + } + + pub fn register_media_instance(&self, media_instance: &HTMLMediaElement) { + self.media_instance.set(Some(media_instance)); + } + + pub fn handle_action(&self, action: MediaSessionActionType) { + debug!("Handle media session action {:?}", action); + + if let Some(handler) = self.action_handlers.borrow().get(&action) { + if handler.Call__(ExceptionHandling::Report).is_err() { + warn!("Error calling MediaSessionActionHandler callback"); + } + return; + } + + // Default action. + if let Some(media) = self.media_instance.get() { + match action { + MediaSessionActionType::Play => { + let in_compartment_proof = AlreadyInCompartment::assert(&self.global()); + media.Play(InCompartment::Already(&in_compartment_proof)); + }, + MediaSessionActionType::Pause => { + media.Pause(); + }, + MediaSessionActionType::SeekBackward => {}, + MediaSessionActionType::SeekForward => {}, + MediaSessionActionType::PreviousTrack => {}, + MediaSessionActionType::NextTrack => {}, + MediaSessionActionType::SkipAd => {}, + MediaSessionActionType::Stop => {}, + MediaSessionActionType::SeekTo => {}, + } + } + } + + 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"); + window.send_to_constellation(ScriptMsg::MediaSessionEvent(pipeline_id, event)); + } + + pub fn update_title(&self, title: String) { + let mut metadata = self.metadata.borrow_mut(); + if let Some(ref mut metadata) = *metadata { + // We only update the title with the data provided by the media + // player and iff the user did not provide a title. + if !metadata.title.is_empty() { + return; + } + metadata.title = title; + } else { + *metadata = Some(EmbedderMediaMetadata::new(title)); + } + self.send_event(MediaSessionEvent::SetMetadata( + metadata.as_ref().unwrap().clone(), + )); + } +} + +impl MediaSessionMethods for MediaSession { + /// https://w3c.github.io/mediasession/#dom-mediasession-metadata + fn GetMetadata(&self) -> Option<DomRoot<MediaMetadata>> { + if let Some(ref metadata) = *self.metadata.borrow() { + let mut init = MediaMetadataInit::empty(); + init.title = DOMString::from_string(metadata.title.clone()); + init.artist = DOMString::from_string(metadata.artist.clone()); + init.album = DOMString::from_string(metadata.album.clone()); + let global = self.global(); + Some(MediaMetadata::new(&global.as_window(), &init)) + } else { + None + } + } + + /// https://w3c.github.io/mediasession/#dom-mediasession-metadata + fn SetMetadata(&self, metadata: Option<&MediaMetadata>) { + if let Some(ref metadata) = metadata { + metadata.set_session(self); + } + + let global = self.global(); + let window = global.as_window(); + let _metadata = match metadata { + Some(m) => { + let title = if m.Title().is_empty() { + window.get_url().into_string() + } else { + m.Title().into() + }; + EmbedderMediaMetadata { + title, + artist: m.Artist().into(), + album: m.Album().into(), + } + }, + None => EmbedderMediaMetadata::new(window.get_url().into_string()), + }; + + *self.metadata.borrow_mut() = Some(_metadata.clone()); + + self.send_event(MediaSessionEvent::SetMetadata(_metadata)); + } + + /// https://w3c.github.io/mediasession/#dom-mediasession-playbackstate + fn PlaybackState(&self) -> MediaSessionPlaybackState { + *self.playback_state.borrow() + } + + /// https://w3c.github.io/mediasession/#dom-mediasession-playbackstate + fn SetPlaybackState(&self, state: MediaSessionPlaybackState) { + *self.playback_state.borrow_mut() = state; + } + + /// https://w3c.github.io/mediasession/#update-action-handler-algorithm + fn SetActionHandler( + &self, + action: MediaSessionAction, + handler: Option<Rc<MediaSessionActionHandler>>, + ) { + match handler { + Some(handler) => self + .action_handlers + .borrow_mut() + .insert(action.into(), handler.clone()), + None => self.action_handlers.borrow_mut().remove(&action.into()), + }; + } +} + +impl From<MediaSessionAction> for MediaSessionActionType { + fn from(action: MediaSessionAction) -> MediaSessionActionType { + match action { + MediaSessionAction::Play => MediaSessionActionType::Play, + MediaSessionAction::Pause => MediaSessionActionType::Pause, + MediaSessionAction::Seekbackward => MediaSessionActionType::SeekBackward, + MediaSessionAction::Seekforward => MediaSessionActionType::SeekForward, + MediaSessionAction::Previoustrack => MediaSessionActionType::PreviousTrack, + MediaSessionAction::Nexttrack => MediaSessionActionType::NextTrack, + MediaSessionAction::Skipad => MediaSessionActionType::SkipAd, + MediaSessionAction::Stop => MediaSessionActionType::Stop, + MediaSessionAction::Seekto => MediaSessionActionType::SeekTo, + } + } +} diff --git a/components/script/dom/messageevent.rs b/components/script/dom/messageevent.rs index cb8ebaaebd1..9d9e5f88e35 100644 --- a/components/script/dom/messageevent.rs +++ b/components/script/dom/messageevent.rs @@ -5,9 +5,10 @@ use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods; use crate::dom::bindings::codegen::Bindings::MessageEventBinding; use crate::dom::bindings::codegen::Bindings::MessageEventBinding::MessageEventMethods; +use crate::dom::bindings::codegen::UnionTypes::WindowProxyOrMessagePortOrServiceWorker; use crate::dom::bindings::error::Fallible; use crate::dom::bindings::inheritance::Castable; -use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::reflector::reflect_dom_object; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::trace::RootedTraceableBox; @@ -16,14 +17,39 @@ use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::messageport::MessagePort; +use crate::dom::serviceworker::ServiceWorker; use crate::dom::windowproxy::WindowProxy; use crate::script_runtime::JSContext; use dom_struct::dom_struct; -use js::jsapi::{Heap, JSObject}; +use js::jsapi::Heap; use js::jsval::JSVal; use js::rust::HandleValue; use servo_atoms::Atom; -use std::ptr::NonNull; + +#[unrooted_must_root_lint::must_root] +#[derive(JSTraceable, MallocSizeOf)] +enum SrcObject { + WindowProxy(Dom<WindowProxy>), + MessagePort(Dom<MessagePort>), + ServiceWorker(Dom<ServiceWorker>), +} + +impl From<&WindowProxyOrMessagePortOrServiceWorker> for SrcObject { + #[allow(unrooted_must_root)] + fn from(src_object: &WindowProxyOrMessagePortOrServiceWorker) -> SrcObject { + match src_object { + WindowProxyOrMessagePortOrServiceWorker::WindowProxy(blob) => { + SrcObject::WindowProxy(Dom::from_ref(&*blob)) + }, + WindowProxyOrMessagePortOrServiceWorker::MessagePort(stream) => { + SrcObject::MessagePort(Dom::from_ref(&*stream)) + }, + WindowProxyOrMessagePortOrServiceWorker::ServiceWorker(stream) => { + SrcObject::ServiceWorker(Dom::from_ref(&*stream)) + }, + } + } +} #[dom_struct] pub struct MessageEvent { @@ -31,7 +57,7 @@ pub struct MessageEvent { #[ignore_malloc_size_of = "mozjs"] data: Heap<JSVal>, origin: DOMString, - source: Option<Dom<WindowProxy>>, + source: Option<SrcObject>, lastEventId: DOMString, ports: Vec<DomRoot<MessagePort>>, } @@ -52,14 +78,14 @@ impl MessageEvent { global: &GlobalScope, data: HandleValue, origin: DOMString, - source: Option<&WindowProxy>, + source: Option<&WindowProxyOrMessagePortOrServiceWorker>, lastEventId: DOMString, ports: Vec<DomRoot<MessagePort>>, ) -> DomRoot<MessageEvent> { let ev = Box::new(MessageEvent { event: Event::new_inherited(), data: Heap::default(), - source: source.map(Dom::from_ref), + source: source.map(|source| source.into()), origin, lastEventId, ports, @@ -77,7 +103,7 @@ impl MessageEvent { cancelable: bool, data: HandleValue, origin: DOMString, - source: Option<&WindowProxy>, + source: Option<&WindowProxyOrMessagePortOrServiceWorker>, lastEventId: DOMString, ports: Vec<DomRoot<MessagePort>>, ) -> DomRoot<MessageEvent> { @@ -94,10 +120,6 @@ impl MessageEvent { type_: DOMString, init: RootedTraceableBox<MessageEventBinding::MessageEventInit>, ) -> Fallible<DomRoot<MessageEvent>> { - let source = init - .source - .as_ref() - .and_then(|inner| inner.as_ref().map(|source| source.window_proxy())); let ev = MessageEvent::new( global, Atom::from(type_), @@ -105,9 +127,9 @@ impl MessageEvent { init.parent.cancelable, init.data.handle(), init.origin.clone(), - source.as_ref().map(|source| &**source), + init.source.as_ref(), init.lastEventId.clone(), - init.ports.clone().unwrap_or(vec![]), + init.ports.clone(), ); Ok(ev) } @@ -129,7 +151,11 @@ impl MessageEvent { false, message, DOMString::from(origin.unwrap_or("")), - source, + source + .map(|source| { + WindowProxyOrMessagePortOrServiceWorker::WindowProxy(DomRoot::from_ref(source)) + }) + .as_ref(), DOMString::new(), ports, ); @@ -138,10 +164,6 @@ impl MessageEvent { pub fn dispatch_error(target: &EventTarget, scope: &GlobalScope) { let init = MessageEventBinding::MessageEventInit::empty(); - let source = init - .source - .as_ref() - .and_then(|inner| inner.as_ref().map(|source| source.window_proxy())); let messageevent = MessageEvent::new( scope, atom!("messageerror"), @@ -149,9 +171,9 @@ impl MessageEvent { init.parent.cancelable, init.data.handle(), init.origin.clone(), - source.as_ref().map(|source| &**source), + init.source.as_ref(), init.lastEventId.clone(), - init.ports.clone().unwrap_or(vec![]), + init.ports.clone(), ); messageevent.upcast::<Event>().fire(target); } @@ -169,10 +191,19 @@ impl MessageEventMethods for MessageEvent { } // https://html.spec.whatwg.org/multipage/#dom-messageevent-source - fn GetSource(&self, _cx: JSContext) -> Option<NonNull<JSObject>> { - self.source - .as_ref() - .and_then(|source| NonNull::new(source.reflector().get_jsobject().get())) + fn GetSource(&self) -> Option<WindowProxyOrMessagePortOrServiceWorker> { + match &self.source { + Some(SrcObject::WindowProxy(i)) => Some( + WindowProxyOrMessagePortOrServiceWorker::WindowProxy(DomRoot::from_ref(&*i)), + ), + Some(SrcObject::MessagePort(i)) => Some( + WindowProxyOrMessagePortOrServiceWorker::MessagePort(DomRoot::from_ref(&*i)), + ), + Some(SrcObject::ServiceWorker(i)) => Some( + WindowProxyOrMessagePortOrServiceWorker::ServiceWorker(DomRoot::from_ref(&*i)), + ), + None => None, + } } /// <https://html.spec.whatwg.org/multipage/#dom-messageevent-lasteventid> diff --git a/components/script/dom/messageport.rs b/components/script/dom/messageport.rs index 67d470a10a2..3198df50923 100644 --- a/components/script/dom/messageport.rs +++ b/components/script/dom/messageport.rs @@ -294,8 +294,6 @@ impl MessagePortMethods for MessagePort { let mut rooted = CustomAutoRooter::new( options .transfer - .as_ref() - .unwrap_or(&Vec::with_capacity(0)) .iter() .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get()) .collect(), diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 302ae5baad4..d348633df88 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -315,6 +315,8 @@ pub mod gamepadbuttonlist; pub mod gamepadevent; pub mod gamepadlist; pub mod globalscope; +pub mod gpu; +pub mod gpuadapter; pub mod hashchangeevent; pub mod headers; pub mod history; @@ -389,16 +391,20 @@ pub mod htmltrackelement; pub mod htmlulistelement; pub mod htmlunknownelement; pub mod htmlvideoelement; +pub mod identityhub; pub mod imagedata; pub mod inputevent; pub mod keyboardevent; pub mod location; pub mod mediadevices; +pub mod mediaelementaudiosourcenode; pub mod mediaerror; pub mod mediafragmentparser; pub mod medialist; +pub mod mediametadata; pub mod mediaquerylist; pub mod mediaquerylistevent; +pub mod mediasession; pub mod mediastream; pub mod mediastreamtrack; pub mod messagechannel; @@ -532,6 +538,7 @@ pub mod webglshader; pub mod webglshaderprecisionformat; pub mod webglsync; pub mod webgltexture; +pub mod webgltransformfeedback; pub mod webgluniformlocation; pub mod webglvertexarrayobjectoes; pub mod websocket; diff --git a/components/script/dom/navigator.rs b/components/script/dom/navigator.rs index 8a0f6a21d98..9121f7ef54d 100644 --- a/components/script/dom/navigator.rs +++ b/components/script/dom/navigator.rs @@ -11,7 +11,10 @@ use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::bindings::str::DOMString; use crate::dom::bluetooth::Bluetooth; use crate::dom::gamepadlist::GamepadList; +use crate::dom::gpu::GPU; +use crate::dom::identityhub::Identities; use crate::dom::mediadevices::MediaDevices; +use crate::dom::mediasession::MediaSession; use crate::dom::mimetypearray::MimeTypeArray; use crate::dom::navigatorinfo; use crate::dom::permissions::Permissions; @@ -21,7 +24,9 @@ use crate::dom::serviceworkercontainer::ServiceWorkerContainer; use crate::dom::window::Window; use crate::dom::xr::XR; use dom_struct::dom_struct; +use std::cell::RefCell; use std::rc::Rc; +use webgpu::wgpu::AdapterId; #[dom_struct] pub struct Navigator { @@ -34,6 +39,10 @@ pub struct Navigator { mediadevices: MutNullableDom<MediaDevices>, gamepads: MutNullableDom<GamepadList>, permissions: MutNullableDom<Permissions>, + mediasession: MutNullableDom<MediaSession>, + gpu: MutNullableDom<GPU>, + #[ignore_malloc_size_of = "Defined in wgpu"] + gpu_id_hub: RefCell<Identities>, } impl Navigator { @@ -48,6 +57,9 @@ impl Navigator { mediadevices: Default::default(), gamepads: Default::default(), permissions: Default::default(), + mediasession: Default::default(), + gpu: Default::default(), + gpu_id_hub: RefCell::new(Identities::new()), } } @@ -60,6 +72,12 @@ impl Navigator { } } +impl Navigator { + pub fn create_adapter_id(&self) -> AdapterId { + self.gpu_id_hub.borrow_mut().create_adapter_id() + } +} + impl NavigatorMethods for Navigator { // https://html.spec.whatwg.org/multipage/#dom-navigator-product fn Product(&self) -> DOMString { @@ -186,4 +204,25 @@ impl NavigatorMethods for Navigator { self.mediadevices .or_init(|| MediaDevices::new(&self.global())) } + + /// https://w3c.github.io/mediasession/#dom-navigator-mediasession + fn MediaSession(&self) -> DomRoot<MediaSession> { + self.mediasession.or_init(|| { + // There is a single MediaSession instance per Pipeline + // and only one active MediaSession globally. + // + // MediaSession creation can happen in two cases: + // + // - If content gets `navigator.mediaSession` + // - If a media instance (HTMLMediaElement so far) starts playing media. + let global = self.global(); + let window = global.as_window(); + MediaSession::new(window) + }) + } + + // https://gpuweb.github.io/gpuweb/#dom-navigator-gpu + fn Gpu(&self) -> DomRoot<GPU> { + self.gpu.or_init(|| GPU::new(&self.global())) + } } diff --git a/components/script/dom/nodelist.rs b/components/script/dom/nodelist.rs index 82fd3be59d6..930fedbbec4 100644 --- a/components/script/dom/nodelist.rs +++ b/components/script/dom/nodelist.rs @@ -13,7 +13,7 @@ use dom_struct::dom_struct; use std::cell::Cell; #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub enum NodeListType { Simple(Vec<Dom<Node>>), Children(ChildrenList), @@ -119,7 +119,7 @@ impl NodeList { } #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct ChildrenList { node: Dom<Node>, #[ignore_malloc_size_of = "Defined in rust-mozjs"] diff --git a/components/script/dom/offscreencanvas.rs b/components/script/dom/offscreencanvas.rs index 142379519fb..2186ba54bc8 100644 --- a/components/script/dom/offscreencanvas.rs +++ b/components/script/dom/offscreencanvas.rs @@ -23,7 +23,7 @@ use ref_filter_map; use std::cell::Cell; use std::cell::Ref; -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(Clone, JSTraceable, MallocSizeOf)] pub enum OffscreenCanvasContext { OffscreenContext2d(Dom<OffscreenCanvasRenderingContext2D>), @@ -92,11 +92,9 @@ impl OffscreenCanvas { OffscreenCanvasContext::OffscreenContext2d(ref ctx) => Some(DomRoot::from_ref(ctx)), }; } - let size = self.get_size(); let context = OffscreenCanvasRenderingContext2D::new( &self.global(), self, - size, self.placeholder.as_ref().map(|c| &**c), ); *self.context.borrow_mut() = Some(OffscreenCanvasContext::OffscreenContext2d( @@ -136,6 +134,14 @@ impl OffscreenCanvasMethods for OffscreenCanvas { // https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-width fn SetWidth(&self, value: u64) { self.width.set(value); + + if let Some(canvas_context) = self.context() { + match &*canvas_context { + OffscreenCanvasContext::OffscreenContext2d(rendering_context) => { + rendering_context.set_canvas_bitmap_dimensions(self.get_size()); + }, + } + } } // https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-height @@ -146,5 +152,13 @@ impl OffscreenCanvasMethods for OffscreenCanvas { // https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-height fn SetHeight(&self, value: u64) { self.height.set(value); + + if let Some(canvas_context) = self.context() { + match &*canvas_context { + OffscreenCanvasContext::OffscreenContext2d(rendering_context) => { + rendering_context.set_canvas_bitmap_dimensions(self.get_size()); + }, + } + } } } diff --git a/components/script/dom/offscreencanvasrenderingcontext2d.rs b/components/script/dom/offscreencanvasrenderingcontext2d.rs index 04e0a13032c..077752d9d6e 100644 --- a/components/script/dom/offscreencanvasrenderingcontext2d.rs +++ b/components/script/dom/offscreencanvasrenderingcontext2d.rs @@ -30,44 +30,32 @@ use euclid::default::Size2D; #[dom_struct] pub struct OffscreenCanvasRenderingContext2D { reflector_: Reflector, - canvas: Option<Dom<OffscreenCanvas>>, + canvas: Dom<OffscreenCanvas>, canvas_state: DomRefCell<CanvasState>, htmlcanvas: Option<Dom<HTMLCanvasElement>>, - width: u32, - height: u32, } impl OffscreenCanvasRenderingContext2D { fn new_inherited( global: &GlobalScope, - canvas: Option<&OffscreenCanvas>, - size: Size2D<u64>, + canvas: &OffscreenCanvas, htmlcanvas: Option<&HTMLCanvasElement>, ) -> OffscreenCanvasRenderingContext2D { OffscreenCanvasRenderingContext2D { reflector_: Reflector::new(), - canvas: canvas.map(Dom::from_ref), + canvas: Dom::from_ref(canvas), htmlcanvas: htmlcanvas.map(Dom::from_ref), - canvas_state: DomRefCell::new(CanvasState::new( - global, - Size2D::new(size.width as u64, size.height as u64), - )), - width: size.width as u32, - height: size.height as u32, + canvas_state: DomRefCell::new(CanvasState::new(global, canvas.get_size())), } } pub fn new( global: &GlobalScope, canvas: &OffscreenCanvas, - size: Size2D<u64>, htmlcanvas: Option<&HTMLCanvasElement>, ) -> DomRoot<OffscreenCanvasRenderingContext2D> { let boxed = Box::new(OffscreenCanvasRenderingContext2D::new_inherited( - global, - Some(canvas), - size, - htmlcanvas, + global, canvas, htmlcanvas, )); reflect_dom_object( boxed, @@ -75,12 +63,21 @@ impl OffscreenCanvasRenderingContext2D { OffscreenCanvasRenderingContext2DBinding::Wrap, ) } + /* + pub fn get_canvas_state(&self) -> Ref<CanvasState> { + self.canvas_state.borrow() + } + */ + + pub fn set_canvas_bitmap_dimensions(&self, size: Size2D<u64>) { + self.canvas_state.borrow().set_bitmap_dimensions(size); + } } impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContext2D { // https://html.spec.whatwg.org/multipage/offscreencontext2d-canvas fn Canvas(&self) -> DomRoot<OffscreenCanvas> { - DomRoot::from_ref(self.canvas.as_ref().expect("No canvas.")) + DomRoot::from_ref(&self.canvas) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-fillrect @@ -315,7 +312,7 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex // https://html.spec.whatwg.org/multipage/#dom-context-2d-getimagedata fn GetImageData(&self, sx: i32, sy: i32, sw: i32, sh: i32) -> Fallible<DomRoot<ImageData>> { self.canvas_state.borrow().get_image_data( - Size2D::new(self.width, self.height), + self.canvas.get_size(), &self.global(), sx, sy, @@ -326,12 +323,9 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata fn PutImageData(&self, imagedata: &ImageData, dx: i32, dy: i32) { - self.canvas_state.borrow().put_image_data( - Size2D::new(self.width, self.height), - imagedata, - dx, - dy, - ) + self.canvas_state + .borrow() + .put_image_data(self.canvas.get_size(), imagedata, dx, dy) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata @@ -347,7 +341,7 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex dirty_height: i32, ) { self.canvas_state.borrow().put_image_data_( - Size2D::new(self.width, self.height), + self.canvas.get_size(), imagedata, dx, dy, diff --git a/components/script/dom/paintrenderingcontext2d.rs b/components/script/dom/paintrenderingcontext2d.rs index 1bbb52d05fe..e4b0a3cf71e 100644 --- a/components/script/dom/paintrenderingcontext2d.rs +++ b/components/script/dom/paintrenderingcontext2d.rs @@ -21,6 +21,7 @@ use crate::dom::canvasgradient::CanvasGradient; use crate::dom::canvaspattern::CanvasPattern; use crate::dom::canvasrenderingcontext2d::CanvasRenderingContext2D; use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope; +use crate::euclidext::Size2DExt; use canvas_traits::canvas::CanvasImageData; use canvas_traits::canvas::CanvasMsg; use canvas_traits::canvas::FromLayoutMsg; @@ -75,7 +76,7 @@ impl PaintRenderingContext2D { let size = size * device_pixel_ratio; self.device_pixel_ratio.set(device_pixel_ratio); self.context - .set_bitmap_dimensions(size.to_untyped().to_u32()); + .set_canvas_bitmap_dimensions(size.to_untyped().to_u64()); self.scale_by_device_pixel_ratio(); } diff --git a/components/script/dom/paintworkletglobalscope.rs b/components/script/dom/paintworkletglobalscope.rs index bc04ab63440..ce3f955cc9c 100644 --- a/components/script/dom/paintworkletglobalscope.rs +++ b/components/script/dom/paintworkletglobalscope.rs @@ -473,7 +473,7 @@ pub enum PaintWorkletTask { /// This type is dangerous, because it contains uboxed `Heap<JSVal>` values, /// which can't be moved. #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] struct PaintDefinition { #[ignore_malloc_size_of = "mozjs"] class_constructor: Heap<JSVal>, diff --git a/components/script/dom/promise.rs b/components/script/dom/promise.rs index b101c449348..d2e209b98dd 100644 --- a/components/script/dom/promise.rs +++ b/components/script/dom/promise.rs @@ -37,7 +37,7 @@ use std::ptr; use std::rc::Rc; #[dom_struct] -#[allow_unrooted_in_rc] +#[unrooted_must_root_lint::allow_unrooted_in_rc] pub struct Promise { reflector: Reflector, /// Since Promise values are natively reference counted without the knowledge of diff --git a/components/script/dom/promiserejectionevent.rs b/components/script/dom/promiserejectionevent.rs index 4e4a148905a..19bf96b9b12 100644 --- a/components/script/dom/promiserejectionevent.rs +++ b/components/script/dom/promiserejectionevent.rs @@ -5,7 +5,7 @@ use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods; use crate::dom::bindings::codegen::Bindings::PromiseRejectionEventBinding; use crate::dom::bindings::codegen::Bindings::PromiseRejectionEventBinding::PromiseRejectionEventMethods; -use crate::dom::bindings::error::{Error, Fallible}; +use crate::dom::bindings::error::Fallible; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::reflect_dom_object; use crate::dom::bindings::root::DomRoot; @@ -72,14 +72,7 @@ impl PromiseRejectionEvent { init: RootedTraceableBox<PromiseRejectionEventBinding::PromiseRejectionEventInit>, ) -> Fallible<DomRoot<Self>> { let reason = init.reason.handle(); - let promise = match init.promise.as_ref() { - Some(promise) => promise.clone(), - None => { - return Err(Error::Type( - "required member promise is undefined.".to_string(), - )); - }, - }; + let promise = init.promise.clone(); let bubbles = EventBubbles::from(init.parent.bubbles); let cancelable = EventCancelable::from(init.parent.cancelable); diff --git a/components/script/dom/range.rs b/components/script/dom/range.rs index 64a26fa0ba9..819cc284d63 100644 --- a/components/script/dom/range.rs +++ b/components/script/dom/range.rs @@ -1011,7 +1011,7 @@ impl RangeMethods for Range { } #[derive(DenyPublicFields, JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct BoundaryPoint { node: MutDom<Node>, offset: Cell<u32>, diff --git a/components/script/dom/raredata.rs b/components/script/dom/raredata.rs index 8c481425982..e32f218ce89 100644 --- a/components/script/dom/raredata.rs +++ b/components/script/dom/raredata.rs @@ -15,7 +15,7 @@ use std::rc::Rc; // storage. #[derive(Default, JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct NodeRareData { /// The shadow root the node belongs to. /// This is None if the node is not in a shadow tree or @@ -28,7 +28,7 @@ pub struct NodeRareData { } #[derive(Default, JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct ElementRareData { /// https://dom.spec.whatwg.org/#dom-element-shadowroot /// The ShadowRoot this element is host of. diff --git a/components/script/dom/response.rs b/components/script/dom/response.rs index d618c9823e4..184e5ff8888 100644 --- a/components/script/dom/response.rs +++ b/components/script/dom/response.rs @@ -403,6 +403,7 @@ impl Response { Some(hyper_headers) => hyper_headers.into_inner(), None => HyperHeaders::new(), }); + *self.mime_type.borrow_mut() = self.Headers().extract_mime_type(); } pub fn set_raw_status(&self, status: Option<(u16, Vec<u8>)>) { diff --git a/components/script/dom/serviceworker.rs b/components/script/dom/serviceworker.rs index ec64036d305..43e7245f3c5 100644 --- a/components/script/dom/serviceworker.rs +++ b/components/script/dom/serviceworker.rs @@ -141,8 +141,6 @@ impl ServiceWorkerMethods for ServiceWorker { let mut rooted = CustomAutoRooter::new( options .transfer - .as_ref() - .unwrap_or(&Vec::with_capacity(0)) .iter() .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get()) .collect(), diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs index 657f43627ed..2a3f4d7041a 100644 --- a/components/script/dom/serviceworkerglobalscope.rs +++ b/components/script/dom/serviceworkerglobalscope.rs @@ -28,24 +28,22 @@ use crate::script_runtime::{ }; use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue}; use crate::task_source::TaskSourceName; -use crossbeam_channel::{unbounded, Receiver, Sender}; +use crossbeam_channel::{after, unbounded, Receiver, Sender}; use devtools_traits::DevtoolScriptControlMsg; use dom_struct::dom_struct; -use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use ipc_channel::ipc::{IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use js::jsapi::{JSContext, JS_AddInterruptCallback}; use js::jsval::UndefinedValue; use msg::constellation_msg::PipelineId; use net_traits::request::{CredentialsMode, Destination, ParserMetadata, Referrer, RequestBuilder}; use net_traits::{CustomResponseMediator, IpcSend}; -use script_traits::{ - ScopeThings, ServiceWorkerMsg, TimerEvent, WorkerGlobalScopeInit, WorkerScriptLoadOrigin, -}; +use script_traits::{ScopeThings, ServiceWorkerMsg, WorkerGlobalScopeInit, WorkerScriptLoadOrigin}; use servo_config::pref; use servo_rand::random; use servo_url::ServoUrl; use std::thread; -use std::time::Duration; +use std::time::{Duration, Instant}; use style::thread_state::{self, ThreadState}; /// Messages used to control service worker event loop @@ -118,7 +116,6 @@ impl QueuedTaskConversion for ServiceWorkerScriptMsg { pub enum MixedMessage { FromServiceWorker(ServiceWorkerScriptMsg), FromDevtools(DevtoolScriptControlMsg), - FromTimeoutThread(()), } #[derive(Clone, JSTraceable)] @@ -147,26 +144,30 @@ unsafe_no_jsmanaged_fields!(TaskQueue<ServiceWorkerScriptMsg>); #[dom_struct] pub struct ServiceWorkerGlobalScope { workerglobalscope: WorkerGlobalScope, + #[ignore_malloc_size_of = "Defined in std"] task_queue: TaskQueue<ServiceWorkerScriptMsg>, + #[ignore_malloc_size_of = "Defined in std"] own_sender: Sender<ServiceWorkerScriptMsg>, + + /// A port on which a single "time-out" message can be received, + /// indicating the sw should stop running, + /// while still draining the task-queue + // and running all enqueued, and not cancelled, tasks. #[ignore_malloc_size_of = "Defined in std"] - timer_event_port: Receiver<()>, + time_out_port: Receiver<Instant>, + #[ignore_malloc_size_of = "Defined in std"] swmanager_sender: IpcSender<ServiceWorkerMsg>, + scope_url: ServoUrl, } impl WorkerEventLoopMethods for ServiceWorkerGlobalScope { - type TimerMsg = (); type WorkerMsg = ServiceWorkerScriptMsg; type Event = MixedMessage; - fn timer_event_port(&self) -> &Receiver<()> { - &self.timer_event_port - } - fn task_queue(&self) -> &TaskQueue<ServiceWorkerScriptMsg> { &self.task_queue } @@ -183,10 +184,6 @@ impl WorkerEventLoopMethods for ServiceWorkerGlobalScope { MixedMessage::FromServiceWorker(msg) } - fn from_timer_msg(&self, msg: ()) -> MixedMessage { - MixedMessage::FromTimeoutThread(msg) - } - fn from_devtools_msg(&self, msg: DevtoolScriptControlMsg) -> MixedMessage { MixedMessage::FromDevtools(msg) } @@ -200,8 +197,7 @@ impl ServiceWorkerGlobalScope { runtime: Runtime, own_sender: Sender<ServiceWorkerScriptMsg>, receiver: Receiver<ServiceWorkerScriptMsg>, - timer_event_chan: IpcSender<TimerEvent>, - timer_event_port: Receiver<()>, + time_out_port: Receiver<Instant>, swmanager_sender: IpcSender<ServiceWorkerMsg>, scope_url: ServoUrl, ) -> ServiceWorkerGlobalScope { @@ -213,12 +209,11 @@ impl ServiceWorkerGlobalScope { worker_url, runtime, from_devtools_receiver, - timer_event_chan, None, ), task_queue: TaskQueue::new(receiver, own_sender.clone()), - timer_event_port: timer_event_port, own_sender: own_sender, + time_out_port, swmanager_sender: swmanager_sender, scope_url: scope_url, } @@ -232,8 +227,7 @@ impl ServiceWorkerGlobalScope { runtime: Runtime, own_sender: Sender<ServiceWorkerScriptMsg>, receiver: Receiver<ServiceWorkerScriptMsg>, - timer_event_chan: IpcSender<TimerEvent>, - timer_event_port: Receiver<()>, + time_out_port: Receiver<Instant>, swmanager_sender: IpcSender<ServiceWorkerMsg>, scope_url: ServoUrl, ) -> DomRoot<ServiceWorkerGlobalScope> { @@ -245,8 +239,7 @@ impl ServiceWorkerGlobalScope { runtime, own_sender, receiver, - timer_event_chan, - timer_event_port, + time_out_port, swmanager_sender, scope_url, )); @@ -315,14 +308,17 @@ impl ServiceWorkerGlobalScope { }, }; - let runtime = new_rt_and_cx(); + let runtime = new_rt_and_cx(None); let (devtools_mpsc_chan, devtools_mpsc_port) = unbounded(); ROUTER .route_ipc_receiver_to_crossbeam_sender(devtools_receiver, devtools_mpsc_chan); - // TODO XXXcreativcoder use this timer_ipc_port, when we have a service worker instance here - let (timer_ipc_chan, _timer_ipc_port) = ipc::channel().unwrap(); - let (timer_chan, timer_port) = unbounded(); + + // Service workers are time limited + // https://w3c.github.io/ServiceWorker/#service-worker-lifetime + let sw_lifetime_timeout = pref!(dom.serviceworker.timeout_seconds) as u64; + let time_out_port = after(Duration::new(sw_lifetime_timeout, 0)); + let global = ServiceWorkerGlobalScope::new( init, url, @@ -330,8 +326,7 @@ impl ServiceWorkerGlobalScope { runtime, own_sender, receiver, - timer_ipc_chan, - timer_port, + time_out_port, swmanager_sender, scope_url, ); @@ -343,15 +338,6 @@ impl ServiceWorkerGlobalScope { } scope.execute_script(DOMString::from(source)); - // Service workers are time limited - thread::Builder::new() - .name("SWTimeoutThread".to_owned()) - .spawn(move || { - let sw_lifetime_timeout = pref!(dom.serviceworker.timeout_seconds) as u64; - thread::sleep(Duration::new(sw_lifetime_timeout, 0)); - let _ = timer_chan.send(()); - }) - .expect("Thread spawning failed"); global.dispatch_activate(); let reporter_name = format!("service-worker-reporter-{}", random::<u64>()); @@ -364,8 +350,9 @@ impl ServiceWorkerGlobalScope { // by inside settings until it is destroyed. // The worker processing model remains on this step // until the event loop is destroyed, - // which happens after the closing flag is set to true. - while !scope.is_closing() { + // which happens after the closing flag is set to true, + // or until the worker has run beyond its allocated time. + while !scope.is_closing() || !global.has_timed_out() { run_worker_event_loop(&*global, None); } }, @@ -398,15 +385,21 @@ impl ServiceWorkerGlobalScope { self.handle_script_event(msg); true }, - MixedMessage::FromTimeoutThread(_) => { - let _ = self - .swmanager_sender - .send(ServiceWorkerMsg::Timeout(self.scope_url.clone())); - false - }, } } + fn has_timed_out(&self) -> bool { + // Note: this should be included in the `select` inside `run_worker_event_loop`, + // otherwise a block on the select can prevent the timeout. + if self.time_out_port.try_recv().is_ok() { + let _ = self + .swmanager_sender + .send(ServiceWorkerMsg::Timeout(self.scope_url.clone())); + return true; + } + false + } + fn handle_script_event(&self, msg: ServiceWorkerScriptMsg) { use self::ServiceWorkerScriptMsg::*; diff --git a/components/script/dom/servoparser/async_html.rs b/components/script/dom/servoparser/async_html.rs index 42296062840..6930f2553c5 100644 --- a/components/script/dom/servoparser/async_html.rs +++ b/components/script/dom/servoparser/async_html.rs @@ -196,7 +196,7 @@ fn create_buffer_queue(mut buffers: VecDeque<SendTendril<UTF8>>) -> BufferQueue // |_____________| |_______________| // #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct Tokenizer { document: Dom<Document>, #[ignore_malloc_size_of = "Defined in std"] diff --git a/components/script/dom/servoparser/html.rs b/components/script/dom/servoparser/html.rs index c9e3a06f34f..22e2574a9b4 100644 --- a/components/script/dom/servoparser/html.rs +++ b/components/script/dom/servoparser/html.rs @@ -30,7 +30,7 @@ use servo_url::ServoUrl; use std::io; #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct Tokenizer { #[ignore_malloc_size_of = "Defined in html5ever"] inner: HtmlTokenizer<TreeBuilder<Dom<Node>, Sink>>, diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index 91b5c9bfce6..004daeed9be 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -626,7 +626,7 @@ enum ParserKind { } #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] enum Tokenizer { Html(self::html::Tokenizer), AsyncHtml(self::async_html::Tokenizer), @@ -945,7 +945,7 @@ fn insert(parent: &Node, reference_child: Option<&Node>, child: NodeOrText<Dom<N } #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct Sink { base_url: ServoUrl, document: Dom<Document>, diff --git a/components/script/dom/servoparser/prefetch.rs b/components/script/dom/servoparser/prefetch.rs index 906c9352771..d9de4d7036f 100644 --- a/components/script/dom/servoparser/prefetch.rs +++ b/components/script/dom/servoparser/prefetch.rs @@ -32,7 +32,7 @@ use servo_url::ImmutableOrigin; use servo_url::ServoUrl; #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct Tokenizer { #[ignore_malloc_size_of = "Defined in html5ever"] inner: HtmlTokenizer<PrefetchSink>, diff --git a/components/script/dom/servoparser/xml.rs b/components/script/dom/servoparser/xml.rs index 8f30e54174d..ac900d61ce0 100644 --- a/components/script/dom/servoparser/xml.rs +++ b/components/script/dom/servoparser/xml.rs @@ -17,7 +17,7 @@ use xml5ever::tokenizer::XmlTokenizer; use xml5ever::tree_builder::{Tracer as XmlTracer, XmlTreeBuilder}; #[derive(JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct Tokenizer { #[ignore_malloc_size_of = "Defined in xml5ever"] inner: XmlTokenizer<XmlTreeBuilder<Dom<Node>, Sink>>, diff --git a/components/script/dom/stylesheetlist.rs b/components/script/dom/stylesheetlist.rs index 236f1c9cd9a..391ac55ed91 100644 --- a/components/script/dom/stylesheetlist.rs +++ b/components/script/dom/stylesheetlist.rs @@ -16,7 +16,7 @@ use dom_struct::dom_struct; use servo_arc::Arc; use style::stylesheets::Stylesheet; -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(JSTraceable, MallocSizeOf)] pub enum StyleSheetListOwner { Document(Dom<Document>), diff --git a/components/script/dom/trackevent.rs b/components/script/dom/trackevent.rs index 684a0fcde52..5a734a0e4c9 100644 --- a/components/script/dom/trackevent.rs +++ b/components/script/dom/trackevent.rs @@ -20,7 +20,7 @@ use crate::dom::window::Window; use dom_struct::dom_struct; use servo_atoms::Atom; -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(JSTraceable, MallocSizeOf)] enum MediaTrack { Video(Dom<VideoTrack>), diff --git a/components/script/dom/url.rs b/components/script/dom/url.rs index 9952739243b..cc6c83e2c18 100644 --- a/components/script/dom/url.rs +++ b/components/script/dom/url.rs @@ -129,13 +129,15 @@ impl URL { let origin = get_blob_origin(&global.get_url()); if let Ok(url) = ServoUrl::parse(&url) { - if let Ok((id, _)) = parse_blob_url(&url) { - let resource_threads = global.resource_threads(); - let (tx, rx) = ipc::channel(global.time_profiler_chan().clone()).unwrap(); - let msg = FileManagerThreadMsg::RevokeBlobURL(id, origin, tx); - let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg)); - - let _ = rx.recv().unwrap(); + if url.fragment().is_none() && origin == get_blob_origin(&url) { + if let Ok((id, _)) = parse_blob_url(&url) { + let resource_threads = global.resource_threads(); + let (tx, rx) = ipc::channel(global.time_profiler_chan().clone()).unwrap(); + let msg = FileManagerThreadMsg::RevokeBlobURL(id, origin, tx); + let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg)); + + let _ = rx.recv().unwrap(); + } } } } diff --git a/components/script/dom/webgl2renderingcontext.rs b/components/script/dom/webgl2renderingcontext.rs index beff6ec6bfd..2c63b775bb1 100644 --- a/components/script/dom/webgl2renderingcontext.rs +++ b/components/script/dom/webgl2renderingcontext.rs @@ -11,8 +11,8 @@ use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer; use crate::dom::bindings::codegen::UnionTypes::Float32ArrayOrUnrestrictedFloatSequence; use crate::dom::bindings::codegen::UnionTypes::ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement; use crate::dom::bindings::codegen::UnionTypes::Int32ArrayOrLongSequence; -use crate::dom::bindings::conversions::ToJSValConvertible; use crate::dom::bindings::error::{ErrorResult, Fallible}; +use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom}; use crate::dom::bindings::str::DOMString; @@ -25,28 +25,32 @@ use crate::dom::webglprogram::WebGLProgram; use crate::dom::webglquery::WebGLQuery; use crate::dom::webglrenderbuffer::WebGLRenderbuffer; use crate::dom::webglrenderingcontext::{ - LayoutCanvasWebGLRenderingContextHelpers, WebGLRenderingContext, + LayoutCanvasWebGLRenderingContextHelpers, Size2DExt, WebGLRenderingContext, }; use crate::dom::webglsampler::{WebGLSampler, WebGLSamplerValue}; use crate::dom::webglshader::WebGLShader; use crate::dom::webglshaderprecisionformat::WebGLShaderPrecisionFormat; use crate::dom::webglsync::WebGLSync; use crate::dom::webgltexture::WebGLTexture; +use crate::dom::webgltransformfeedback::WebGLTransformFeedback; use crate::dom::webgluniformlocation::WebGLUniformLocation; 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, }; use dom_struct::dom_struct; -use euclid::default::Size2D; +use euclid::default::{Point2D, Rect, Size2D}; use ipc_channel::ipc; use js::jsapi::{JSObject, Type}; use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, NullValue, UInt32Value}; use js::rust::CustomAutoRooterGuard; use js::typedarray::ArrayBufferView; use script_layout_interface::HTMLCanvasDataSource; +use std::cell::Cell; +use std::cmp; use std::ptr::NonNull; #[dom_struct] @@ -62,6 +66,10 @@ pub struct WebGL2RenderingContext { bound_pixel_unpack_buffer: MutNullableDom<WebGLBuffer>, bound_transform_feedback_buffer: MutNullableDom<WebGLBuffer>, bound_uniform_buffer: MutNullableDom<WebGLBuffer>, + current_transform_feedback: MutNullableDom<WebGLTransformFeedback>, + texture_pack_row_length: Cell<usize>, + texture_pack_skip_pixels: Cell<usize>, + texture_pack_skip_rows: Cell<usize>, } fn typedarray_elem_size(typeid: Type) -> usize { @@ -74,6 +82,17 @@ fn typedarray_elem_size(typeid: Type) -> usize { } } +struct ReadPixelsAllowedFormats<'a> { + array_types: &'a [Type], + channels: usize, +} + +struct ReadPixelsSizes { + row_stride: usize, + skipped_bytes: usize, + size: usize, +} + impl WebGL2RenderingContext { fn new_inherited( window: &Window, @@ -100,6 +119,10 @@ impl WebGL2RenderingContext { bound_pixel_unpack_buffer: MutNullableDom::new(None), bound_transform_feedback_buffer: MutNullableDom::new(None), bound_uniform_buffer: MutNullableDom::new(None), + 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), }) } @@ -143,6 +166,213 @@ impl WebGL2RenderingContext { slot.set(None); } } + + fn calc_read_pixel_formats( + &self, + pixel_type: u32, + format: u32, + ) -> WebGLResult<ReadPixelsAllowedFormats> { + let array_types = match pixel_type { + constants::BYTE => &[Type::Int8][..], + constants::SHORT => &[Type::Int16][..], + constants::INT => &[Type::Int32][..], + constants::UNSIGNED_BYTE => &[Type::Uint8, Type::Uint8Clamped][..], + constants::UNSIGNED_SHORT | + constants::UNSIGNED_SHORT_4_4_4_4 | + constants::UNSIGNED_SHORT_5_5_5_1 | + constants::UNSIGNED_SHORT_5_6_5 => &[Type::Uint16][..], + constants::UNSIGNED_INT | + constants::UNSIGNED_INT_2_10_10_10_REV | + constants::UNSIGNED_INT_10F_11F_11F_REV | + constants::UNSIGNED_INT_5_9_9_9_REV => &[Type::Uint32][..], + constants::FLOAT => &[Type::Float32][..], + constants::HALF_FLOAT => &[Type::Uint16][..], + _ => return Err(InvalidEnum), + }; + let channels = match format { + constants::ALPHA | constants::RED | constants::RED_INTEGER => 1, + constants::RG | constants::RG_INTEGER => 2, + constants::RGB | constants::RGB_INTEGER => 3, + constants::RGBA | constants::RGBA_INTEGER => 4, + _ => return Err(InvalidEnum), + }; + Ok(ReadPixelsAllowedFormats { + array_types, + channels, + }) + } + + fn calc_read_pixel_sizes( + &self, + width: i32, + height: i32, + bytes_per_pixel: usize, + ) -> WebGLResult<ReadPixelsSizes> { + if width < 0 || height < 0 { + return Err(InvalidValue); + } + + // See also https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.36 + let pixels_per_row = if self.texture_pack_row_length.get() > 0 { + self.texture_pack_row_length.get() + } else { + width as usize + }; + if self.texture_pack_skip_pixels.get() + width as usize > pixels_per_row { + return Err(InvalidOperation); + } + + let bytes_per_row = pixels_per_row + .checked_mul(bytes_per_pixel) + .ok_or(InvalidOperation)?; + let row_padding_bytes = { + let pack_alignment = self.base.get_texture_packing_alignment() as usize; + match bytes_per_row % pack_alignment { + 0 => 0, + remainder => pack_alignment - remainder, + } + }; + let row_stride = bytes_per_row + row_padding_bytes; + let size = if width == 0 || height == 0 { + 0 + } else { + let full_row_bytes = row_stride + .checked_mul(height as usize - 1) + .ok_or(InvalidOperation)?; + let last_row_bytes = bytes_per_pixel + .checked_mul(width as usize) + .ok_or(InvalidOperation)?; + let result = full_row_bytes + .checked_add(last_row_bytes) + .ok_or(InvalidOperation)?; + result + }; + let skipped_bytes = { + let skipped_row_bytes = self + .texture_pack_skip_rows + .get() + .checked_mul(row_stride) + .ok_or(InvalidOperation)?; + let skipped_pixel_bytes = self + .texture_pack_skip_pixels + .get() + .checked_mul(bytes_per_pixel) + .ok_or(InvalidOperation)?; + let result = skipped_row_bytes + .checked_add(skipped_pixel_bytes) + .ok_or(InvalidOperation)?; + result + }; + Ok(ReadPixelsSizes { + row_stride, + skipped_bytes, + size, + }) + } + + #[allow(unsafe_code)] + fn read_pixels_into( + &self, + x: i32, + y: i32, + width: i32, + height: i32, + format: u32, + pixel_type: u32, + dst: &mut ArrayBufferView, + dst_elem_offset: u32, + ) { + handle_potential_webgl_error!(self.base, self.base.validate_framebuffer(), return); + + if self.bound_pixel_pack_buffer.get().is_some() { + 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 + }; + if dst_byte_offset > dst.len() { + return self.base.webgl_error(InvalidValue); + } + + let dst_array_type = dst.get_array_type(); + let ReadPixelsAllowedFormats { + array_types: allowed_array_types, + channels, + } = match self.calc_read_pixel_formats(pixel_type, format) { + Ok(result) => result, + Err(error) => return self.base.webgl_error(error), + }; + if !allowed_array_types.contains(&dst_array_type) { + return self.base.webgl_error(InvalidOperation); + } + if format != constants::RGBA || pixel_type != constants::UNSIGNED_BYTE { + return self.base.webgl_error(InvalidOperation); + } + + let bytes_per_pixel = typedarray_elem_size(dst_array_type) * channels; + let ReadPixelsSizes { + row_stride, + skipped_bytes, + size, + } = match self.calc_read_pixel_sizes(width, height, bytes_per_pixel) { + Ok(result) => result, + Err(error) => return self.base.webgl_error(error), + }; + let dst_end = dst_byte_offset + skipped_bytes + size; + let dst_pixels = unsafe { dst.as_mut_slice() }; + if dst_pixels.len() < dst_end { + return self.base.webgl_error(InvalidOperation); + } + + let dst_byte_offset = { + let margin_left = cmp::max(0, -x) as usize; + let margin_top = cmp::max(0, -y) as usize; + dst_byte_offset + + skipped_bytes + + margin_left * bytes_per_pixel + + margin_top * row_stride + }; + let src_rect = { + let (fb_width, fb_height) = handle_potential_webgl_error!( + self.base, + self.base + .get_current_framebuffer_size() + .ok_or(InvalidOperation), + return + ); + let src_origin = Point2D::new(x, y); + let src_size = Size2D::new(width as u32, height as u32); + let fb_size = Size2D::new(fb_width as u32, fb_height as u32); + match pixels::clip(src_origin, src_size.to_u64(), fb_size.to_u64()) { + Some(rect) => rect.to_u32(), + None => return, + } + }; + let src_row_bytes = handle_potential_webgl_error!( + self.base, + src_rect + .size + .width + .checked_mul(bytes_per_pixel as u32) + .ok_or(InvalidOperation), + return + ); + + let (sender, receiver) = ipc::bytes_channel().unwrap(); + self.base.send_command(WebGLCommand::ReadPixels( + src_rect, format, pixel_type, sender, + )); + let src = receiver.recv().unwrap(); + + for i in 0..src_rect.size.height as usize { + let src_start = i * src_row_bytes as usize; + let dst_start = dst_byte_offset + i * row_stride; + dst_pixels[dst_start..dst_start + src_row_bytes as usize] + .copy_from_slice(&src[src_start..src_start + src_row_bytes as usize]); + } + } } impl WebGL2RenderingContextMethods for WebGL2RenderingContext { @@ -210,6 +440,9 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { constants::UNIFORM_BUFFER_BINDING => unsafe { optional_root_object_to_js_or_null!(*cx, &self.bound_uniform_buffer.get()) }, + constants::TRANSFORM_FEEDBACK_BINDING => unsafe { + optional_root_object_to_js_or_null!(*cx, self.current_transform_feedback.get()) + }, _ => self.base.GetParameter(cx, parameter), } } @@ -824,7 +1057,24 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 fn GetProgramParameter(&self, cx: JSContext, program: &WebGLProgram, param_id: u32) -> JSVal { - self.base.GetProgramParameter(cx, program, param_id) + handle_potential_webgl_error!( + self.base, + self.base.validate_ownership(program), + return NullValue() + ); + if program.is_deleted() { + self.base.webgl_error(InvalidOperation); + return NullValue(); + } + match param_id { + constants::TRANSFORM_FEEDBACK_VARYINGS => { + Int32Value(program.transform_feedback_varyings_length()) + }, + constants::TRANSFORM_FEEDBACK_BUFFER_MODE => { + Int32Value(program.transform_feedback_buffer_mode()) + }, + _ => self.base.GetProgramParameter(cx, program, param_id), + } } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 @@ -912,9 +1162,18 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { self.base.LineWidth(width) } - /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.2 fn PixelStorei(&self, param_name: u32, param_value: i32) { - self.base.PixelStorei(param_name, param_value) + if param_value < 0 { + return self.base.webgl_error(InvalidValue); + } + + match param_name { + constants::PACK_ROW_LENGTH => self.texture_pack_row_length.set(param_value as _), + constants::PACK_SKIP_PIXELS => self.texture_pack_skip_pixels.set(param_value as _), + constants::PACK_SKIP_ROWS => self.texture_pack_skip_rows.set(param_value as _), + _ => self.base.PixelStorei(param_name, param_value), + } } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 @@ -931,10 +1190,128 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { height: i32, format: u32, pixel_type: u32, - pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>, + mut pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>, ) { - self.base - .ReadPixels(x, y, width, height, format, pixel_type, pixels) + let pixels = + handle_potential_webgl_error!(self.base, pixels.as_mut().ok_or(InvalidValue), return); + + self.read_pixels_into(x, y, width, height, format, pixel_type, pixels, 0) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.10 + fn ReadPixels_( + &self, + x: i32, + y: i32, + width: i32, + height: i32, + format: u32, + pixel_type: u32, + dst_byte_offset: i64, + ) { + handle_potential_webgl_error!(self.base, self.base.validate_framebuffer(), return); + + let dst = match self.bound_pixel_pack_buffer.get() { + Some(buffer) => buffer, + None => return self.base.webgl_error(InvalidOperation), + }; + + if dst_byte_offset < 0 { + return self.base.webgl_error(InvalidValue); + } + let dst_byte_offset = dst_byte_offset as usize; + if dst_byte_offset > dst.capacity() { + return self.base.webgl_error(InvalidOperation); + } + + let ReadPixelsAllowedFormats { + array_types: _, + channels: bytes_per_pixel, + } = match self.calc_read_pixel_formats(pixel_type, format) { + Ok(result) => result, + Err(error) => return self.base.webgl_error(error), + }; + if format != constants::RGBA || pixel_type != constants::UNSIGNED_BYTE { + return self.base.webgl_error(InvalidOperation); + } + + let ReadPixelsSizes { + row_stride: _, + skipped_bytes, + size, + } = match self.calc_read_pixel_sizes(width, height, bytes_per_pixel) { + Ok(result) => result, + Err(error) => return self.base.webgl_error(error), + }; + let dst_end = dst_byte_offset + skipped_bytes + size; + if dst.capacity() < dst_end { + return self.base.webgl_error(InvalidOperation); + } + + { + let (fb_width, fb_height) = handle_potential_webgl_error!( + self.base, + self.base + .get_current_framebuffer_size() + .ok_or(InvalidOperation), + return + ); + let src_origin = Point2D::new(x, y); + let src_size = Size2D::new(width as u32, height as u32); + let fb_size = Size2D::new(fb_width as u32, fb_height as u32); + if pixels::clip(src_origin, src_size.to_u64(), fb_size.to_u64()).is_none() { + return; + } + } + let src_rect = Rect::new(Point2D::new(x, y), Size2D::new(width, height)); + + self.base.send_command(WebGLCommand::PixelStorei( + constants::PACK_ALIGNMENT, + self.base.get_texture_packing_alignment() as _, + )); + self.base.send_command(WebGLCommand::PixelStorei( + constants::PACK_ROW_LENGTH, + self.texture_pack_row_length.get() as _, + )); + self.base.send_command(WebGLCommand::PixelStorei( + constants::PACK_SKIP_ROWS, + self.texture_pack_skip_rows.get() as _, + )); + self.base.send_command(WebGLCommand::PixelStorei( + constants::PACK_SKIP_PIXELS, + self.texture_pack_skip_pixels.get() as _, + )); + self.base.send_command(WebGLCommand::ReadPixelsPP( + src_rect, + format, + pixel_type, + dst_byte_offset, + )); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.10 + #[allow(unsafe_code)] + fn ReadPixels__( + &self, + x: i32, + y: i32, + width: i32, + height: i32, + format: u32, + pixel_type: u32, + mut dst: CustomAutoRooterGuard<ArrayBufferView>, + dst_elem_offset: u32, + ) { + self.read_pixels_into( + x, + y, + width, + height, + format, + pixel_type, + &mut dst, + dst_elem_offset, + ) } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 @@ -1721,6 +2098,212 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { }, } } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + fn CreateTransformFeedback(&self) -> Option<DomRoot<WebGLTransformFeedback>> { + Some(WebGLTransformFeedback::new(&self.base)) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + fn DeleteTransformFeedback(&self, tf: Option<&WebGLTransformFeedback>) { + if let Some(tf) = tf { + handle_potential_webgl_error!(self.base, self.base.validate_ownership(tf), return); + if tf.is_active() { + self.base.webgl_error(InvalidOperation); + return; + } + tf.delete(false); + self.current_transform_feedback.set(None); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + fn IsTransformFeedback(&self, tf: Option<&WebGLTransformFeedback>) -> bool { + match tf { + Some(tf) => { + if !tf.is_valid() { + return false; + } + handle_potential_webgl_error!( + self.base, + self.base.validate_ownership(tf), + return false + ); + let (sender, receiver) = webgl_channel().unwrap(); + self.base + .send_command(WebGLCommand::IsTransformFeedback(tf.id(), sender)); + receiver.recv().unwrap() + }, + None => false, + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + fn BindTransformFeedback(&self, target: u32, tf: Option<&WebGLTransformFeedback>) { + if target != constants::TRANSFORM_FEEDBACK { + self.base.webgl_error(InvalidEnum); + return; + } + match tf { + Some(transform_feedback) => { + handle_potential_webgl_error!( + self.base, + self.base.validate_ownership(transform_feedback), + return + ); + if !transform_feedback.is_valid() { + self.base.webgl_error(InvalidOperation); + return; + } + if let Some(current_tf) = self.current_transform_feedback.get() { + if current_tf.is_active() && !current_tf.is_paused() { + self.base.webgl_error(InvalidOperation); + return; + } + } + transform_feedback.bind(&self.base, target); + self.current_transform_feedback + .set(Some(transform_feedback)); + }, + None => self + .base + .send_command(WebGLCommand::BindTransformFeedback(target, 0)), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + fn BeginTransformFeedback(&self, primitiveMode: u32) { + match primitiveMode { + constants::POINTS | constants::LINES | constants::TRIANGLES => {}, + _ => { + self.base.webgl_error(InvalidEnum); + return; + }, + }; + let current_tf = match self.current_transform_feedback.get() { + Some(current_tf) => current_tf, + None => { + self.base.webgl_error(InvalidOperation); + return; + }, + }; + if current_tf.is_active() { + self.base.webgl_error(InvalidOperation); + return; + }; + let program = match self.base.current_program() { + Some(program) => program, + None => { + self.base.webgl_error(InvalidOperation); + return; + }, + }; + if !program.is_linked() || program.transform_feedback_varyings_length() != 0 { + self.base.webgl_error(InvalidOperation); + return; + }; + current_tf.begin(&self.base, primitiveMode); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + fn EndTransformFeedback(&self) { + if let Some(current_tf) = self.current_transform_feedback.get() { + if !current_tf.is_active() { + self.base.webgl_error(InvalidOperation); + return; + } + current_tf.end(&self.base); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + fn ResumeTransformFeedback(&self) { + if let Some(current_tf) = self.current_transform_feedback.get() { + if !current_tf.is_active() || !current_tf.is_paused() { + self.base.webgl_error(InvalidOperation); + return; + } + current_tf.resume(&self.base); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + fn PauseTransformFeedback(&self) { + if let Some(current_tf) = self.current_transform_feedback.get() { + if !current_tf.is_active() || current_tf.is_paused() { + self.base.webgl_error(InvalidOperation); + return; + } + current_tf.pause(&self.base); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + fn TransformFeedbackVaryings( + &self, + program: &WebGLProgram, + varyings: Vec<DOMString>, + bufferMode: u32, + ) { + handle_potential_webgl_error!(self.base, program.validate(), return); + let strs = varyings + .iter() + .map(|name| String::from(name.to_owned())) + .collect::<Vec<String>>(); + match bufferMode { + constants::INTERLEAVED_ATTRIBS => { + self.base + .send_command(WebGLCommand::TransformFeedbackVaryings( + program.id(), + strs, + bufferMode, + )); + }, + constants::SEPARATE_ATTRIBS => { + let max_tf_sp_att = + self.base.limits().max_transform_feedback_separate_attribs as usize; + if strs.len() >= max_tf_sp_att { + self.base.webgl_error(InvalidValue); + return; + } + self.base + .send_command(WebGLCommand::TransformFeedbackVaryings( + program.id(), + strs, + bufferMode, + )); + }, + _ => self.base.webgl_error(InvalidEnum), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15 + fn GetTransformFeedbackVarying( + &self, + program: &WebGLProgram, + index: u32, + ) -> Option<DomRoot<WebGLActiveInfo>> { + handle_potential_webgl_error!(self.base, program.validate(), return None); + if index >= program.transform_feedback_varyings_length() as u32 { + self.base.webgl_error(InvalidValue); + return None; + } + + let (sender, receiver) = webgl_channel().unwrap(); + self.base + .send_command(WebGLCommand::GetTransformFeedbackVarying( + program.id(), + index, + sender, + )); + let (size, ty, name) = receiver.recv().unwrap(); + Some(WebGLActiveInfo::new( + self.base.global().as_window(), + size, + ty, + DOMString::from(name), + )) + } } impl LayoutCanvasWebGLRenderingContextHelpers for LayoutDom<WebGL2RenderingContext> { diff --git a/components/script/dom/webgl_extensions/extensions.rs b/components/script/dom/webgl_extensions/extensions.rs index fa502ca26cd..b7a2f2beb80 100644 --- a/components/script/dom/webgl_extensions/extensions.rs +++ b/components/script/dom/webgl_extensions/extensions.rs @@ -140,7 +140,7 @@ impl WebGLExtensionFeatures { } /// Handles the list of implemented, supported and enabled WebGL extensions. -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(JSTraceable, MallocSizeOf)] pub struct WebGLExtensions { extensions: DomRefCell<HashMap<String, Box<dyn WebGLExtensionWrapper>>>, diff --git a/components/script/dom/webgl_extensions/wrapper.rs b/components/script/dom/webgl_extensions/wrapper.rs index 6af68c4678e..bf8db31418c 100644 --- a/components/script/dom/webgl_extensions/wrapper.rs +++ b/components/script/dom/webgl_extensions/wrapper.rs @@ -28,7 +28,7 @@ pub trait WebGLExtensionWrapper: JSTraceable + MallocSizeOf { fn as_any(&self) -> &dyn Any; } -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(JSTraceable, MallocSizeOf)] pub struct TypedWebGLExtensionWrapper<T: WebGLExtension> { extension: MutNullableDom<T::Extension>, diff --git a/components/script/dom/webglframebuffer.rs b/components/script/dom/webglframebuffer.rs index df9e194962e..def5b36a27f 100644 --- a/components/script/dom/webglframebuffer.rs +++ b/components/script/dom/webglframebuffer.rs @@ -29,7 +29,7 @@ pub enum CompleteForRendering { MissingColorAttachment, } -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(Clone, JSTraceable, MallocSizeOf)] enum WebGLFramebufferAttachment { Renderbuffer(Dom<WebGLRenderbuffer>), diff --git a/components/script/dom/webglprogram.rs b/components/script/dom/webglprogram.rs index 6863bc89e53..e4f5f89c6d3 100644 --- a/components/script/dom/webglprogram.rs +++ b/components/script/dom/webglprogram.rs @@ -34,6 +34,8 @@ pub struct WebGLProgram { vertex_shader: MutNullableDom<WebGLShader>, active_attribs: DomRefCell<Box<[ActiveAttribInfo]>>, active_uniforms: DomRefCell<Box<[ActiveUniformInfo]>>, + transform_feedback_varyings_length: Cell<i32>, + transform_feedback_mode: Cell<i32>, } impl WebGLProgram { @@ -50,6 +52,8 @@ impl WebGLProgram { vertex_shader: Default::default(), active_attribs: DomRefCell::new(vec![].into()), active_uniforms: DomRefCell::new(vec![].into()), + transform_feedback_varyings_length: Default::default(), + transform_feedback_mode: Default::default(), } } @@ -187,6 +191,10 @@ impl WebGLProgram { self.linked.set(link_info.linked); self.link_called.set(true); + self.transform_feedback_varyings_length + .set(link_info.transform_feedback_length); + self.transform_feedback_mode + .set(link_info.transform_feedback_mode); *self.active_attribs.borrow_mut() = link_info.active_attribs; *self.active_uniforms.borrow_mut() = link_info.active_uniforms; Ok(()) @@ -444,6 +452,14 @@ impl WebGLProgram { pub fn link_generation(&self) -> u64 { self.link_generation.get() } + + pub fn transform_feedback_varyings_length(&self) -> i32 { + self.transform_feedback_varyings_length.get() + } + + pub fn transform_feedback_buffer_mode(&self) -> i32 { + self.transform_feedback_mode.get() + } } impl Drop for WebGLProgram { diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index f0e4887858c..85930cd3529 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -380,7 +380,7 @@ impl WebGLRenderingContext { // // The WebGL spec mentions a couple more operations that trigger // this: clear() and getParameter(IMPLEMENTATION_COLOR_READ_*). - fn validate_framebuffer(&self) -> WebGLResult<()> { + pub fn validate_framebuffer(&self) -> WebGLResult<()> { match self.bound_framebuffer.get() { Some(fb) => match fb.check_status_for_rendering() { CompleteForRendering::Complete => Ok(()), @@ -481,7 +481,7 @@ impl WebGLRenderingContext { self.send_command(WebGLCommand::VertexAttrib(indx, x, y, z, w)); } - fn get_current_framebuffer_size(&self) -> Option<(i32, i32)> { + pub fn get_current_framebuffer_size(&self) -> Option<(i32, i32)> { match self.bound_framebuffer.get() { Some(fb) => return fb.size(), @@ -490,6 +490,10 @@ impl WebGLRenderingContext { } } + pub fn get_texture_packing_alignment(&self) -> u8 { + self.texture_packing_alignment.get() + } + // LINEAR filtering may be forbidden when using WebGL extensions. // https://www.khronos.org/registry/webgl/extensions/OES_texture_float_linear/ fn validate_filterable_texture( @@ -1165,6 +1169,10 @@ impl WebGLRenderingContext { slot.set(buffer); } + + pub fn current_program(&self) -> Option<DomRoot<WebGLProgram>> { + self.current_program.get() + } } #[cfg(not(feature = "webgl_backtrace"))] @@ -2959,11 +2967,16 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { let src_origin = Point2D::new(x, y); let src_size = Size2D::new(width as u32, height as u32); let fb_size = Size2D::new(fb_width as u32, fb_height as u32); - let src_rect = match pixels::clip(src_origin, src_size, fb_size) { + let src_rect = match pixels::clip(src_origin, src_size.to_u64(), fb_size.to_u64()) { Some(rect) => rect, None => return, }; + // Note: we're casting a Rect<u64> back into a Rect<u32> here, but it's okay because + // it used u32 data types to begin with. It just got converted to Rect<u64> in + // pixels::clip + let src_rect = src_rect.to_u32(); + let mut dest_offset = 0; if x < 0 { dest_offset += -x * bytes_per_pixel; @@ -4266,7 +4279,7 @@ capabilities! { STENCIL_TEST, } -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(JSTraceable, MallocSizeOf)] pub struct Textures { active_unit: Cell<u32>, @@ -4334,7 +4347,7 @@ impl Textures { } } -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(Default, JSTraceable, MallocSizeOf)] struct TextureUnit { tex_2d: MutNullableDom<WebGLTexture>, @@ -4481,3 +4494,13 @@ impl WebGLMessageSender { self.wake_after_send(|| self.sender.send_dom_to_texture(command)) } } + +pub trait Size2DExt { + fn to_u64(&self) -> Size2D<u64>; +} + +impl Size2DExt for Size2D<u32> { + fn to_u64(&self) -> Size2D<u64> { + return Size2D::new(self.width as u64, self.height as u64); + } +} diff --git a/components/script/dom/webgltransformfeedback.rs b/components/script/dom/webgltransformfeedback.rs new file mode 100644 index 00000000000..a58a8e43577 --- /dev/null +++ b/components/script/dom/webgltransformfeedback.rs @@ -0,0 +1,133 @@ +/* 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::codegen::Bindings::WebGLTransformFeedbackBinding; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::webglobject::WebGLObject; +use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use canvas_traits::webgl::{webgl_channel, WebGLCommand}; +use dom_struct::dom_struct; +use std::cell::Cell; + +#[dom_struct] +pub struct WebGLTransformFeedback { + webgl_object: WebGLObject, + id: u32, + marked_for_deletion: Cell<bool>, + has_been_bound: Cell<bool>, + is_active: Cell<bool>, + is_paused: Cell<bool>, +} + +impl WebGLTransformFeedback { + fn new_inherited(context: &WebGLRenderingContext, id: u32) -> Self { + Self { + webgl_object: WebGLObject::new_inherited(context), + id, + marked_for_deletion: Cell::new(false), + has_been_bound: Cell::new(false), + is_active: Cell::new(false), + is_paused: Cell::new(false), + } + } + + pub fn new(context: &WebGLRenderingContext) -> DomRoot<Self> { + let (sender, receiver) = webgl_channel().unwrap(); + context.send_command(WebGLCommand::CreateTransformFeedback(sender)); + let id = receiver.recv().unwrap(); + + reflect_dom_object( + Box::new(WebGLTransformFeedback::new_inherited(context, id)), + &*context.global(), + WebGLTransformFeedbackBinding::Wrap, + ) + } +} + +impl WebGLTransformFeedback { + pub fn bind(&self, context: &WebGLRenderingContext, target: u32) { + context.send_command(WebGLCommand::BindTransformFeedback(target, self.id())); + self.has_been_bound.set(true); + } + + pub fn begin(&self, context: &WebGLRenderingContext, primitive_mode: u32) { + if self.has_been_bound.get() && !self.is_active() { + context.send_command(WebGLCommand::BeginTransformFeedback(primitive_mode)); + self.set_active(true); + } + } + + pub fn end(&self, context: &WebGLRenderingContext) { + if self.has_been_bound.get() && self.is_active() { + if self.is_paused() { + context.send_command(WebGLCommand::ResumeTransformFeedback()); + } + context.send_command(WebGLCommand::EndTransformFeedback()); + self.set_active(false); + } + } + + pub fn resume(&self, context: &WebGLRenderingContext) { + if self.is_active() && self.is_paused() { + context.send_command(WebGLCommand::ResumeTransformFeedback()); + self.set_pause(false); + } + } + + pub fn pause(&self, context: &WebGLRenderingContext) { + if self.is_active() && !self.is_paused() { + context.send_command(WebGLCommand::PauseTransformFeedback()); + self.set_pause(true); + } + } + + pub fn id(&self) -> u32 { + self.id + } + + pub fn is_valid(&self) -> bool { + !self.marked_for_deletion.get() + } + + pub fn is_active(&self) -> bool { + self.is_active.get() + } + + pub fn is_paused(&self) -> bool { + self.is_paused.get() + } + + pub fn delete(&self, fallible: bool) { + if self.is_valid() && self.id() != 0 { + self.marked_for_deletion.set(true); + let context = self.upcast::<WebGLObject>().context(); + let cmd = WebGLCommand::DeleteTransformFeedback(self.id); + if fallible { + context.send_command_ignored(cmd); + } else { + context.send_command(cmd); + } + } + } + + pub fn set_active(&self, value: bool) { + if self.is_valid() && self.has_been_bound.get() { + self.is_active.set(value); + } + } + + pub fn set_pause(&self, value: bool) { + if self.is_valid() && self.is_active() { + self.is_active.set(value); + } + } +} + +impl Drop for WebGLTransformFeedback { + fn drop(&mut self) { + self.delete(true); + } +} diff --git a/components/script/dom/webglvertexarrayobjectoes.rs b/components/script/dom/webglvertexarrayobjectoes.rs index 22d162782d8..e54eaf3f04a 100644 --- a/components/script/dom/webglvertexarrayobjectoes.rs +++ b/components/script/dom/webglvertexarrayobjectoes.rs @@ -258,7 +258,7 @@ impl Drop for WebGLVertexArrayObjectOES { } #[derive(Clone, JSTraceable, MallocSizeOf)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct VertexAttribData { pub enabled_as_array: bool, pub size: u8, diff --git a/components/script/dom/webidls/AudioContext.webidl b/components/script/dom/webidls/AudioContext.webidl index 9e5dd6bd556..cd9e18edfa0 100644 --- a/components/script/dom/webidls/AudioContext.webidl +++ b/components/script/dom/webidls/AudioContext.webidl @@ -33,7 +33,7 @@ interface AudioContext : BaseAudioContext { Promise<void> suspend(); Promise<void> close(); - // MediaElementAudioSourceNode createMediaElementSource(HTMLMediaElement mediaElement); + [Throws] MediaElementAudioSourceNode createMediaElementSource(HTMLMediaElement mediaElement); // MediaStreamAudioSourceNode createMediaStreamSource(MediaStream mediaStream); // MediaStreamTrackAudioSourceNode createMediaStreamTrackSource(MediaStreamTrack mediaStreamTrack); // MediaStreamAudioDestinationNode createMediaStreamDestination(); diff --git a/components/script/dom/webidls/Bluetooth.webidl b/components/script/dom/webidls/Bluetooth.webidl index a24206036bf..e5e135c0577 100644 --- a/components/script/dom/webidls/Bluetooth.webidl +++ b/components/script/dom/webidls/Bluetooth.webidl @@ -21,7 +21,7 @@ dictionary BluetoothLEScanFilterInit { dictionary RequestDeviceOptions { sequence<BluetoothLEScanFilterInit> filters; - sequence<BluetoothServiceUUID> optionalServices /*= []*/; + sequence<BluetoothServiceUUID> optionalServices = []; boolean acceptAllDevices = false; }; diff --git a/components/script/dom/webidls/BluetoothPermissionResult.webidl b/components/script/dom/webidls/BluetoothPermissionResult.webidl index 4f9f2871a2e..95c06797aef 100644 --- a/components/script/dom/webidls/BluetoothPermissionResult.webidl +++ b/components/script/dom/webidls/BluetoothPermissionResult.webidl @@ -8,7 +8,7 @@ dictionary BluetoothPermissionDescriptor : PermissionDescriptor { DOMString deviceId; // These match RequestDeviceOptions. sequence<BluetoothLEScanFilterInit> filters; - sequence<BluetoothServiceUUID> optionalServices/* = []*/; + sequence<BluetoothServiceUUID> optionalServices = []; boolean acceptAllDevices = false; }; diff --git a/components/script/dom/webidls/DissimilarOriginWindow.webidl b/components/script/dom/webidls/DissimilarOriginWindow.webidl index bbfcb75b69b..b690736469a 100644 --- a/components/script/dom/webidls/DissimilarOriginWindow.webidl +++ b/components/script/dom/webidls/DissimilarOriginWindow.webidl @@ -25,7 +25,7 @@ interface DissimilarOriginWindow : GlobalScope { void close(); readonly attribute boolean closed; - [Throws] void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer /*= []*/); + [Throws] void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer = []); [Throws] void postMessage(any message, optional WindowPostMessageOptions options = {}); attribute any opener; void blur(); diff --git a/components/script/dom/webidls/FormDataEvent.webidl b/components/script/dom/webidls/FormDataEvent.webidl index 5160a396081..0cb81b93962 100644 --- a/components/script/dom/webidls/FormDataEvent.webidl +++ b/components/script/dom/webidls/FormDataEvent.webidl @@ -5,10 +5,10 @@ // https://html.spec.whatwg.org/multipage/#the-formdataevent-interface [Exposed=Window] interface FormDataEvent : Event { - [Throws] constructor(DOMString type, optional FormDataEventInit eventInitDict = {}); + [Throws] constructor(DOMString type, FormDataEventInit eventInitDict); readonly attribute FormData formData; }; dictionary FormDataEventInit : EventInit { - /*required*/ FormData formData; + required FormData formData; }; diff --git a/components/script/dom/webidls/GPU.webidl b/components/script/dom/webidls/GPU.webidl new file mode 100644 index 00000000000..856c5027569 --- /dev/null +++ b/components/script/dom/webidls/GPU.webidl @@ -0,0 +1,21 @@ +/* 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://gpuweb.github.io/gpuweb/#gpu-interface +[Exposed=(Window, DedicatedWorker), Pref="dom.webgpu.enabled"] +interface GPU { + // May reject with DOMException // TODO: DOMException("OperationError")? + Promise<GPUAdapter> requestAdapter(optional GPURequestAdapterOptions options = {}); +}; + +// https://gpuweb.github.io/gpuweb/#dictdef-gpurequestadapteroptions +dictionary GPURequestAdapterOptions { + GPUPowerPreference powerPreference; +}; + +// https://gpuweb.github.io/gpuweb/#enumdef-gpupowerpreference +enum GPUPowerPreference { + "low-power", + "high-performance" +}; diff --git a/components/script/dom/webidls/GPUAdapter.webidl b/components/script/dom/webidls/GPUAdapter.webidl new file mode 100644 index 00000000000..0c2118a5f56 --- /dev/null +++ b/components/script/dom/webidls/GPUAdapter.webidl @@ -0,0 +1,14 @@ +/* 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://gpuweb.github.io/gpuweb/#gpuadapter +[Exposed=(Window, DedicatedWorker), Pref="dom.webgpu.enabled"] +interface GPUAdapter { + readonly attribute DOMString name; + readonly attribute object extensions; + //readonly attribute GPULimits limits; Don’t expose higher limits for now. + + // May reject with DOMException // TODO: DOMException("OperationError")? + // Promise<GPUDevice> requestDevice(optional GPUDeviceDescriptor descriptor = {}); +}; diff --git a/components/script/dom/webidls/HTMLIFrameElement.webidl b/components/script/dom/webidls/HTMLIFrameElement.webidl index acc4f0f201d..b9dd97bdea6 100644 --- a/components/script/dom/webidls/HTMLIFrameElement.webidl +++ b/components/script/dom/webidls/HTMLIFrameElement.webidl @@ -9,8 +9,8 @@ interface HTMLIFrameElement : HTMLElement { [CEReactions] attribute USVString src; - // [CEReactions] - // attribute DOMString srcdoc; + [CEReactions] + attribute DOMString srcdoc; [CEReactions] attribute DOMString name; diff --git a/components/script/dom/webidls/MediaElementAudioSourceNode.webidl b/components/script/dom/webidls/MediaElementAudioSourceNode.webidl new file mode 100644 index 00000000000..5afe7775caa --- /dev/null +++ b/components/script/dom/webidls/MediaElementAudioSourceNode.webidl @@ -0,0 +1,17 @@ +/* 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/. */ +/* + * The origin of this IDL file is + * https://webaudio.github.io/web-audio-api/#mediaelementaudiosourcenode + */ + +dictionary MediaElementAudioSourceOptions { + required HTMLMediaElement mediaElement; +}; + +[Exposed=Window] +interface MediaElementAudioSourceNode : AudioNode { + [Throws] constructor (AudioContext context, MediaElementAudioSourceOptions options); + [SameObject] readonly attribute HTMLMediaElement mediaElement; +}; diff --git a/components/script/dom/webidls/MediaMetadata.webidl b/components/script/dom/webidls/MediaMetadata.webidl new file mode 100644 index 00000000000..495aeef8e35 --- /dev/null +++ b/components/script/dom/webidls/MediaMetadata.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/. */ +/* + * The origin of this IDL file is + * https://w3c.github.io/mediasession/#mediametadata + */ + +dictionary MediaImage { + required USVString src; + DOMString sizes = ""; + DOMString type = ""; +}; + +[Exposed=Window] +interface MediaMetadata { + [Throws] constructor(optional MediaMetadataInit init = {}); + attribute DOMString title; + attribute DOMString artist; + attribute DOMString album; + // TODO: https://github.com/servo/servo/issues/10072 + // attribute FrozenArray<MediaImage> artwork; +}; + +dictionary MediaMetadataInit { + DOMString title = ""; + DOMString artist = ""; + DOMString album = ""; + sequence<MediaImage> artwork = []; +}; diff --git a/components/script/dom/webidls/MediaSession.webidl b/components/script/dom/webidls/MediaSession.webidl new file mode 100644 index 00000000000..12b3fe062ba --- /dev/null +++ b/components/script/dom/webidls/MediaSession.webidl @@ -0,0 +1,57 @@ +/* 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/. */ +/* + * The origin of this IDL file is + * https://w3c.github.io/mediasession/#mediasession + */ + +[Exposed=Window] +partial interface Navigator { + [SameObject] readonly attribute MediaSession mediaSession; +}; + +enum MediaSessionPlaybackState { + "none", + "paused", + "playing" +}; + +enum MediaSessionAction { + "play", + "pause", + "seekbackward", + "seekforward", + "previoustrack", + "nexttrack", + "skipad", + "stop", + "seekto" +}; + +dictionary MediaSessionActionDetails { + required MediaSessionAction action; +}; + +dictionary MediaSessionSeekActionDetails : MediaSessionActionDetails { + double? seekOffset; +}; + +dictionary MediaSessionSeekToActionDetails : MediaSessionActionDetails { + required double seekTime; + boolean? fastSeek; +}; + +callback MediaSessionActionHandler = void(/*MediaSessionActionDetails details*/); + +[Exposed=Window] +interface MediaSession { + attribute MediaMetadata? metadata; + + attribute MediaSessionPlaybackState playbackState; + + void setActionHandler(MediaSessionAction action, MediaSessionActionHandler? handler); + + //void setPositionState(optional MediaPositionState? state); +}; + diff --git a/components/script/dom/webidls/MessageEvent.webidl b/components/script/dom/webidls/MessageEvent.webidl index 63dd9019cc9..1d4699cbe3c 100644 --- a/components/script/dom/webidls/MessageEvent.webidl +++ b/components/script/dom/webidls/MessageEvent.webidl @@ -9,9 +9,7 @@ interface MessageEvent : Event { readonly attribute any data; readonly attribute DOMString origin; readonly attribute DOMString lastEventId; - // FIXME(#22617): WindowProxy is not exposed in Worker globals - readonly attribute object? source; - //readonly attribute (WindowProxy or MessagePort)? source; + readonly attribute MessageEventSource? source; readonly attribute /*FrozenArray<MessagePort>*/any ports; }; @@ -20,9 +18,8 @@ dictionary MessageEventInit : EventInit { DOMString origin = ""; DOMString lastEventId = ""; //DOMString channel; - Window? source; - //(WindowProxy or MessagePort)? source; - sequence<MessagePort> ports; + MessageEventSource? source = null; + sequence<MessagePort> ports = []; }; -typedef (/*WindowProxy or */MessagePort or ServiceWorker) MessageEventSource; +typedef (WindowProxy or MessagePort or ServiceWorker) MessageEventSource; diff --git a/components/script/dom/webidls/MessagePort.webidl b/components/script/dom/webidls/MessagePort.webidl index c2fd1dbd1f4..58a3f06a960 100644 --- a/components/script/dom/webidls/MessagePort.webidl +++ b/components/script/dom/webidls/MessagePort.webidl @@ -8,7 +8,7 @@ [Exposed=(Window,Worker)] interface MessagePort : EventTarget { - [Throws] void postMessage(any message, sequence<object> transfer /*= []*/); + [Throws] void postMessage(any message, sequence<object> transfer); [Throws] void postMessage(any message, optional PostMessageOptions options = {}); void start(); void close(); @@ -19,5 +19,5 @@ interface MessagePort : EventTarget { }; dictionary PostMessageOptions { - sequence<object> transfer; + sequence<object> transfer = []; }; diff --git a/components/script/dom/webidls/Navigator.webidl b/components/script/dom/webidls/Navigator.webidl index 6b5b1e1f283..b51e2ba52f0 100644 --- a/components/script/dom/webidls/Navigator.webidl +++ b/components/script/dom/webidls/Navigator.webidl @@ -75,3 +75,8 @@ partial interface Navigator { partial interface Navigator { [Pref="dom.gamepad.enabled"] GamepadList getGamepads(); }; + +[Exposed=Window] +partial interface Navigator { + [SameObject, Pref="dom.webgpu.enabled"] readonly attribute GPU gpu; +}; diff --git a/components/script/dom/webidls/PromiseRejectionEvent.webidl b/components/script/dom/webidls/PromiseRejectionEvent.webidl index 6ef93b8b1a7..70d11b1ff33 100644 --- a/components/script/dom/webidls/PromiseRejectionEvent.webidl +++ b/components/script/dom/webidls/PromiseRejectionEvent.webidl @@ -6,12 +6,12 @@ [Exposed=(Window,Worker)] interface PromiseRejectionEvent : Event { - [Throws] constructor(DOMString type, optional PromiseRejectionEventInit eventInitDict = {}); + [Throws] constructor(DOMString type, PromiseRejectionEventInit eventInitDict); readonly attribute Promise<any> promise; readonly attribute any reason; }; dictionary PromiseRejectionEventInit : EventInit { - /* required */ Promise<any> promise; + required Promise<any> promise; any reason; }; diff --git a/components/script/dom/webidls/WebGL2RenderingContext.webidl b/components/script/dom/webidls/WebGL2RenderingContext.webidl index c171a9bb866..71b5ab10034 100644 --- a/components/script/dom/webidls/WebGL2RenderingContext.webidl +++ b/components/script/dom/webidls/WebGL2RenderingContext.webidl @@ -12,9 +12,6 @@ typedef long long GLint64; typedef unsigned long long GLuint64; -// interface WebGLTransformFeedback : WebGLObject { -// }; - // interface WebGLVertexArrayObject : WebGLObject { // }; @@ -500,10 +497,10 @@ interface mixin WebGL2RenderingContextBase void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, /*[AllowShared]*/ ArrayBufferView? dstData); // WebGL2: - // void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, - // GLintptr offset); - // void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, - // [AllowShared] ArrayBufferView dstData, GLuint dstOffset); + void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, + GLintptr offset); + void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, + /*[AllowShared]*/ ArrayBufferView dstData, GLuint dstOffset); /* Multiple Render Targets */ // void drawBuffers(sequence<GLenum> buffers); @@ -544,7 +541,7 @@ interface mixin WebGL2RenderingContextBase any getSyncParameter(WebGLSync sync, GLenum pname); /* Transform Feedback */ - /*WebGLTransformFeedback? createTransformFeedback(); + WebGLTransformFeedback? createTransformFeedback(); void deleteTransformFeedback(WebGLTransformFeedback? tf); [WebGLHandlesContextLoss] GLboolean isTransformFeedback(WebGLTransformFeedback? tf); void bindTransformFeedback (GLenum target, WebGLTransformFeedback? tf); @@ -553,7 +550,7 @@ interface mixin WebGL2RenderingContextBase void transformFeedbackVaryings(WebGLProgram program, sequence<DOMString> varyings, GLenum bufferMode); WebGLActiveInfo? getTransformFeedbackVarying(WebGLProgram program, GLuint index); void pauseTransformFeedback(); - void resumeTransformFeedback();*/ + void resumeTransformFeedback(); /* Uniform Buffer Objects and Transform Feedback Buffers */ // void bindBufferBase(GLenum target, GLuint index, WebGLBuffer? buffer); diff --git a/components/script/dom/webidls/WebGLTransformFeedback.webidl b/components/script/dom/webidls/WebGLTransformFeedback.webidl new file mode 100644 index 00000000000..871c0f15c7b --- /dev/null +++ b/components/script/dom/webidls/WebGLTransformFeedback.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.15 +// + +[Exposed=(Window), Pref="dom.webgl2.enabled"] +interface WebGLTransformFeedback : WebGLObject { +}; diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl index 26edbca5c7a..f2cdeeb18a5 100644 --- a/components/script/dom/webidls/Window.webidl +++ b/components/script/dom/webidls/Window.webidl @@ -64,7 +64,7 @@ void cancelAnimationFrame(unsigned long handle); [Throws] - void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer /*= []*/); + void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer = []); [Throws] void postMessage(any message, optional WindowPostMessageOptions options = {}); @@ -175,7 +175,6 @@ partial interface Window { readonly attribute unsigned long runningAnimationCount; }; -dictionary WindowPostMessageOptions { +dictionary WindowPostMessageOptions : PostMessageOptions { USVString targetOrigin = "/"; - sequence<object> transfer; }; diff --git a/components/script/dom/webidls/WindowOrWorkerGlobalScope.webidl b/components/script/dom/webidls/WindowOrWorkerGlobalScope.webidl index b4cc66705af..c798b56ab00 100644 --- a/components/script/dom/webidls/WindowOrWorkerGlobalScope.webidl +++ b/components/script/dom/webidls/WindowOrWorkerGlobalScope.webidl @@ -4,8 +4,7 @@ // https://html.spec.whatwg.org/multipage/#windoworworkerglobalscope -// FIXME(nox): https://github.com/servo/servo/issues/20700 -// typedef (DOMString or Function) TimerHandler; +typedef (DOMString or Function) TimerHandler; [Exposed=(Window,Worker)] interface mixin WindowOrWorkerGlobalScope { @@ -16,13 +15,9 @@ interface mixin WindowOrWorkerGlobalScope { [Throws] DOMString atob(DOMString data); // timers - // FIXME(nox): https://github.com/servo/servo/issues/20700 - long setTimeout(Function handler, optional long timeout = 0, any... arguments); - long setTimeout(DOMString handler, optional long timeout = 0, any... arguments); + long setTimeout(TimerHandler handler, optional long timeout = 0, any... arguments); void clearTimeout(optional long handle = 0); - // FIXME(nox): https://github.com/servo/servo/issues/20700 - long setInterval(Function handler, optional long timeout = 0, any... arguments); - long setInterval(DOMString handler, optional long timeout = 0, any... arguments); + long setInterval(TimerHandler handler, optional long timeout = 0, any... arguments); void clearInterval(optional long handle = 0); // ImageBitmap diff --git a/components/script/dom/webidls/WorkerNavigator.webidl b/components/script/dom/webidls/WorkerNavigator.webidl index 119e22ea11f..5be43960802 100644 --- a/components/script/dom/webidls/WorkerNavigator.webidl +++ b/components/script/dom/webidls/WorkerNavigator.webidl @@ -15,3 +15,8 @@ WorkerNavigator includes NavigatorLanguage; partial interface WorkerNavigator { [Pref="dom.permissions.enabled"] readonly attribute Permissions permissions; }; + +[Exposed=DedicatedWorker] +partial interface WorkerNavigator { + [SameObject, Pref="dom.webgpu.enabled"] readonly attribute GPU gpu; +}; diff --git a/components/script/dom/webidls/XRSession.webidl b/components/script/dom/webidls/XRSession.webidl index be8fb36e7bf..e726a500cb7 100644 --- a/components/script/dom/webidls/XRSession.webidl +++ b/components/script/dom/webidls/XRSession.webidl @@ -39,8 +39,11 @@ interface XRSession : EventTarget { // // Events attribute EventHandler onend; attribute EventHandler onselect; + attribute EventHandler onsqueeze; // attribute EventHandler oninputsourceschange; attribute EventHandler onselectstart; attribute EventHandler onselectend; + attribute EventHandler onsqueezestart; + attribute EventHandler onsqueezeend; attribute EventHandler onvisibilitychange; }; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 37b5b5e3b2c..d48f16ea662 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -7,7 +7,6 @@ use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::DocumentBinding::{ DocumentMethods, DocumentReadyState, }; -use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function; 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; @@ -16,7 +15,7 @@ use crate::dom::bindings::codegen::Bindings::WindowBinding::{ self, FrameRequestCallback, WindowMethods, WindowPostMessageOptions, }; use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions}; -use crate::dom::bindings::codegen::UnionTypes::RequestOrUSVString; +use crate::dom::bindings::codegen::UnionTypes::{RequestOrUSVString, StringOrFunction}; use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; @@ -89,7 +88,7 @@ use js::jsval::{JSVal, NullValue}; use js::rust::wrappers::JS_DefineProperty; use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue}; use media::WindowGLContext; -use msg::constellation_msg::PipelineId; +use msg::constellation_msg::{BrowsingContextId, PipelineId}; use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse}; use net_traits::image_cache::{PendingImageId, PendingImageResponse}; use net_traits::storage_thread::StorageType; @@ -107,10 +106,9 @@ use script_layout_interface::{PendingImageState, TrustedNodeAddress}; use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; use script_traits::{ConstellationControlMsg, DocumentState, HistoryEntryReplacement, LoadData}; use script_traits::{ - ScriptMsg, ScriptToConstellationChan, ScrollState, StructuredSerializedData, TimerEvent, - TimerEventId, + ScriptMsg, ScriptToConstellationChan, ScrollState, StructuredSerializedData, TimerEventId, }; -use script_traits::{TimerSchedulerMsg, WindowSizeData, WindowSizeType}; +use script_traits::{TimerSchedulerMsg, WebrenderIpcSender, WindowSizeData, WindowSizeType}; use selectors::attr::CaseSensitivity; use servo_geometry::{f32_rect_to_au_rect, MaxRect}; use servo_url::{Host, ImmutableOrigin, MutableOrigin, ServoUrl}; @@ -137,8 +135,9 @@ use style::str::HTML_SPACE_CHARACTERS; use style::stylesheets::CssRuleType; use style_traits::{CSSPixel, DevicePixel, ParsingMode}; use url::Position; +use webgpu::WebGPU; use webrender_api::units::{DeviceIntPoint, DeviceIntSize, LayoutPixel}; -use webrender_api::{DocumentId, ExternalScrollId, RenderApiSender}; +use webrender_api::{DocumentId, ExternalScrollId}; use webvr_traits::WebVRMsg; /// Current state of the window object @@ -268,6 +267,10 @@ pub struct Window { #[ignore_malloc_size_of = "channels are hard"] webgl_chan: Option<WebGLChan>, + #[ignore_malloc_size_of = "channels are hard"] + /// A handle for communicating messages to the WebGPU threads. + webgpu: Option<WebGPU>, + /// A handle for communicating messages to the webvr thread, if available. #[ignore_malloc_size_of = "channels are hard"] webvr_chan: Option<IpcSender<WebVRMsg>>, @@ -300,8 +303,8 @@ pub struct Window { exists_mut_observer: Cell<bool>, /// Webrender API Sender - #[ignore_malloc_size_of = "defined in webrender_api"] - webrender_api_sender: RenderApiSender, + #[ignore_malloc_size_of = "Wraps an IpcSender"] + webrender_api_sender: WebrenderIpcSender, /// Indicate whether a SetDocumentStatus message has been sent after a reflow is complete. /// It is used to avoid sending idle message more than once, which is unneccessary. @@ -463,6 +466,10 @@ impl Window { .map(|chan| WebGLCommandSender::new(chan.clone(), self.get_event_loop_waker())) } + pub fn webgpu_channel(&self) -> Option<WebGPU> { + self.webgpu.clone() + } + pub fn webvr_thread(&self) -> Option<IpcSender<WebVRMsg>> { self.webvr_chan.clone() } @@ -506,7 +513,7 @@ impl Window { self.add_pending_reflow(); } - pub fn get_webrender_api_sender(&self) -> RenderApiSender { + pub fn get_webrender_api_sender(&self) -> WebrenderIpcSender { self.webrender_api_sender.clone() } @@ -817,28 +824,16 @@ impl WindowMethods for Window { fn SetTimeout( &self, _cx: JSContext, - callback: Rc<Function>, - timeout: i32, - args: Vec<HandleValue>, - ) -> i32 { - self.upcast::<GlobalScope>().set_timeout_or_interval( - TimerCallback::FunctionTimerCallback(callback), - args, - timeout, - IsInterval::NonInterval, - ) - } - - // https://html.spec.whatwg.org/multipage/#dom-windowtimers-settimeout - fn SetTimeout_( - &self, - _cx: JSContext, - callback: DOMString, + callback: StringOrFunction, timeout: i32, args: Vec<HandleValue>, ) -> i32 { + let callback = match callback { + StringOrFunction::String(i) => TimerCallback::StringTimerCallback(i), + StringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i), + }; self.upcast::<GlobalScope>().set_timeout_or_interval( - TimerCallback::StringTimerCallback(callback), + callback, args, timeout, IsInterval::NonInterval, @@ -855,28 +850,16 @@ impl WindowMethods for Window { fn SetInterval( &self, _cx: JSContext, - callback: Rc<Function>, - timeout: i32, - args: Vec<HandleValue>, - ) -> i32 { - self.upcast::<GlobalScope>().set_timeout_or_interval( - TimerCallback::FunctionTimerCallback(callback), - args, - timeout, - IsInterval::Interval, - ) - } - - // https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval - fn SetInterval_( - &self, - _cx: JSContext, - callback: DOMString, + callback: StringOrFunction, timeout: i32, args: Vec<HandleValue>, ) -> i32 { + let callback = match callback { + StringOrFunction::String(i) => TimerCallback::StringTimerCallback(i), + StringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i), + }; self.upcast::<GlobalScope>().set_timeout_or_interval( - TimerCallback::StringTimerCallback(callback), + callback, args, timeout, IsInterval::Interval, @@ -979,19 +962,13 @@ impl WindowMethods for Window { cx: JSContext, message: HandleValue, target_origin: USVString, - mut transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>, + transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>, ) -> ErrorResult { let incumbent = GlobalScope::incumbent().expect("no incumbent global?"); let source = incumbent.as_window(); let source_origin = source.Document().origin().immutable().clone(); - if transfer.is_some() { - let mut rooted = CustomAutoRooter::new(transfer.take().unwrap()); - let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted)); - self.post_message_impl(&target_origin, source_origin, source, cx, message, transfer) - } else { - self.post_message_impl(&target_origin, source_origin, source, cx, message, None) - } + self.post_message_impl(&target_origin, source_origin, source, cx, message, transfer) } /// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage> @@ -1003,14 +980,13 @@ impl WindowMethods for Window { ) -> ErrorResult { let mut rooted = CustomAutoRooter::new( options + .parent .transfer - .as_ref() - .unwrap_or(&Vec::with_capacity(0)) .iter() .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get()) .collect(), ); - let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted)); + let transfer = CustomAutoRooterGuard::new(*cx, &mut rooted); let incumbent = GlobalScope::incumbent().expect("no incumbent global?"); let source = incumbent.as_window(); @@ -1330,10 +1306,10 @@ impl Window { source: &Window, cx: JSContext, message: HandleValue, - transfer: Option<CustomAutoRooterGuard<Vec<*mut JSObject>>>, + transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>, ) -> ErrorResult { // Step 1-2, 6-8. - let data = structuredclone::write(cx, message, transfer)?; + let data = structuredclone::write(cx, message, Some(transfer))?; // Step 3-5. let target_origin = match target_origin.0[..].as_ref() { @@ -1421,7 +1397,18 @@ impl Window { self.current_state.set(WindowState::Zombie); *self.js_runtime.borrow_mut() = None; - self.window_proxy.set(None); + + // If this is the currently active pipeline, + // nullify the window_proxy. + if let Some(proxy) = self.window_proxy.get() { + let pipeline_id = self.upcast::<GlobalScope>().pipeline_id(); + if let Some(currently_active) = proxy.currently_active() { + if currently_active == pipeline_id { + self.window_proxy.set(None); + } + } + } + if let Some(performance) = self.performance.get() { performance.clear_and_disable_performance_entry_buffer(); } @@ -1836,6 +1823,16 @@ impl Window { DOMString::from(resolved) } + pub fn inner_window_dimensions_query( + &self, + browsing_context: BrowsingContextId, + ) -> Option<Size2D<f32, CSSPixel>> { + if !self.layout_reflow(QueryMsg::InnerWindowDimensionsQuery(browsing_context)) { + return None; + } + self.layout_rpc.inner_window_dimensions() + } + #[allow(unsafe_code)] pub fn offset_parent_query(&self, node: &Node) -> (Option<DomRoot<Element>>, UntypedRect<Au>) { if !self.layout_reflow(QueryMsg::OffsetParentQuery(node.to_opaque())) { @@ -2208,7 +2205,6 @@ impl Window { constellation_chan: ScriptToConstellationChan, control_chan: IpcSender<ConstellationControlMsg>, scheduler_chan: IpcSender<TimerSchedulerMsg>, - timer_event_chan: IpcSender<TimerEvent>, layout_chan: Sender<Msg>, pipelineid: PipelineId, parent_info: Option<PipelineId>, @@ -2217,11 +2213,12 @@ impl Window { navigation_start: u64, navigation_start_precise: u64, webgl_chan: Option<WebGLChan>, + webgpu: Option<WebGPU>, webvr_chan: Option<IpcSender<WebVRMsg>>, webxr_registry: webxr_api::Registry, microtask_queue: Rc<MicrotaskQueue>, webrender_document: DocumentId, - webrender_api_sender: RenderApiSender, + webrender_api_sender: WebrenderIpcSender, layout_is_busy: Arc<AtomicBool>, relayout_event: bool, prepare_for_screenshot: bool, @@ -2251,7 +2248,6 @@ impl Window { constellation_chan, scheduler_chan, resource_threads, - timer_event_chan, origin, microtask_queue, is_headless, @@ -2296,6 +2292,7 @@ impl Window { media_query_lists: DOMTracker::new(), test_runner: Default::default(), webgl_chan, + webgpu, webvr_chan, webxr_registry, permission_state_invocation_results: Default::default(), @@ -2366,6 +2363,7 @@ fn debug_reflow_events(id: PipelineId, reflow_goal: &ReflowGoal, reason: &Reflow &QueryMsg::StyleQuery(_n) => "\tStyleQuery", &QueryMsg::TextIndexQuery(..) => "\tTextIndexQuery", &QueryMsg::ElementInnerTextQuery(_) => "\tElementInnerTextQuery", + &QueryMsg::InnerWindowDimensionsQuery(_) => "\tInnerWindowDimensionsQuery", }, }); diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs index 3b9830f8ded..f08bdcab79e 100644 --- a/components/script/dom/worker.rs +++ b/components/script/dom/worker.rs @@ -218,8 +218,6 @@ impl WorkerMethods for Worker { let mut rooted = CustomAutoRooter::new( options .transfer - .as_ref() - .unwrap_or(&Vec::with_capacity(0)) .iter() .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get()) .collect(), diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 50cb8bb5aaf..e943b6b0ef4 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -4,11 +4,10 @@ use crate::compartments::InCompartment; use crate::dom::bindings::cell::DomRefCell; -use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function; use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit; use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType; use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods; -use crate::dom::bindings::codegen::UnionTypes::RequestOrUSVString; +use crate::dom::bindings::codegen::UnionTypes::{RequestOrUSVString, StringOrFunction}; use crate::dom::bindings::error::{report_pending_exception, Error, ErrorResult, Fallible}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::DomObject; @@ -35,6 +34,7 @@ use crate::task_source::networking::NetworkingTaskSource; use crate::task_source::performance_timeline::PerformanceTimelineTaskSource; use crate::task_source::port_message::PortMessageQueue; use crate::task_source::remote_event::RemoteEventTaskSource; +use crate::task_source::timer::TimerTaskSource; use crate::task_source::websocket::WebsocketTaskSource; use crate::timers::{IsInterval, TimerCallback}; use crossbeam_channel::Receiver; @@ -51,7 +51,6 @@ use net_traits::request::{ }; use net_traits::IpcSend; use script_traits::WorkerGlobalScopeInit; -use script_traits::{TimerEvent, TimerEventId}; use servo_url::{MutableOrigin, ServoUrl}; use std::cell::Ref; use std::default::Default; @@ -121,7 +120,6 @@ impl WorkerGlobalScope { worker_url: ServoUrl, runtime: Runtime, from_devtools_receiver: Receiver<DevtoolScriptControlMsg>, - timer_event_chan: IpcSender<TimerEvent>, closing: Option<Arc<AtomicBool>>, ) -> Self { // Install a pipeline-namespace in the current thread. @@ -135,7 +133,6 @@ impl WorkerGlobalScope { init.script_to_constellation_chan, init.scheduler_chan, init.resource_threads, - timer_event_chan, MutableOrigin::new(init.origin), runtime.microtask_queue.clone(), init.is_headless, @@ -297,28 +294,16 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope { fn SetTimeout( &self, _cx: JSContext, - callback: Rc<Function>, + callback: StringOrFunction, timeout: i32, args: Vec<HandleValue>, ) -> i32 { + let callback = match callback { + StringOrFunction::String(i) => TimerCallback::StringTimerCallback(i), + StringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i), + }; self.upcast::<GlobalScope>().set_timeout_or_interval( - TimerCallback::FunctionTimerCallback(callback), - args, - timeout, - IsInterval::NonInterval, - ) - } - - // https://html.spec.whatwg.org/multipage/#dom-windowtimers-settimeout - fn SetTimeout_( - &self, - _cx: JSContext, - callback: DOMString, - timeout: i32, - args: Vec<HandleValue>, - ) -> i32 { - self.upcast::<GlobalScope>().set_timeout_or_interval( - TimerCallback::StringTimerCallback(callback), + callback, args, timeout, IsInterval::NonInterval, @@ -335,28 +320,16 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope { fn SetInterval( &self, _cx: JSContext, - callback: Rc<Function>, - timeout: i32, - args: Vec<HandleValue>, - ) -> i32 { - self.upcast::<GlobalScope>().set_timeout_or_interval( - TimerCallback::FunctionTimerCallback(callback), - args, - timeout, - IsInterval::Interval, - ) - } - - // https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval - fn SetInterval_( - &self, - _cx: JSContext, - callback: DOMString, + callback: StringOrFunction, timeout: i32, args: Vec<HandleValue>, ) -> i32 { + let callback = match callback { + StringOrFunction::String(i) => TimerCallback::StringTimerCallback(i), + StringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i), + }; self.upcast::<GlobalScope>().set_timeout_or_interval( - TimerCallback::StringTimerCallback(callback), + callback, args, timeout, IsInterval::Interval, @@ -462,6 +435,10 @@ impl WorkerGlobalScope { PortMessageQueue(self.script_chan(), self.pipeline_id()) } + pub fn timer_task_source(&self) -> TimerTaskSource { + TimerTaskSource(self.script_chan(), self.pipeline_id()) + } + pub fn remote_event_task_source(&self) -> RemoteEventTaskSource { RemoteEventTaskSource(self.script_chan(), self.pipeline_id()) } @@ -491,10 +468,6 @@ impl WorkerGlobalScope { } } - pub fn handle_fire_timer(&self, timer_id: TimerEventId) { - self.upcast::<GlobalScope>().fire_timer(timer_id); - } - pub fn close(&self) { if let Some(ref closing) = self.closing { closing.store(true, Ordering::SeqCst); diff --git a/components/script/dom/workernavigator.rs b/components/script/dom/workernavigator.rs index 92dfa9951e5..cc1dfee13bd 100644 --- a/components/script/dom/workernavigator.rs +++ b/components/script/dom/workernavigator.rs @@ -7,6 +7,7 @@ use crate::dom::bindings::codegen::Bindings::WorkerNavigatorBinding::WorkerNavig use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::bindings::str::DOMString; +use crate::dom::gpu::GPU; use crate::dom::navigatorinfo; use crate::dom::permissions::Permissions; use crate::dom::workerglobalscope::WorkerGlobalScope; @@ -17,6 +18,7 @@ use dom_struct::dom_struct; pub struct WorkerNavigator { reflector_: Reflector, permissions: MutNullableDom<Permissions>, + gpu: MutNullableDom<GPU>, } impl WorkerNavigator { @@ -24,6 +26,7 @@ impl WorkerNavigator { WorkerNavigator { reflector_: Reflector::new(), permissions: Default::default(), + gpu: Default::default(), } } @@ -97,4 +100,9 @@ impl WorkerNavigatorMethods for WorkerNavigator { self.permissions .or_init(|| Permissions::new(&self.global())) } + + // https://gpuweb.github.io/gpuweb/#dom-navigator-gpu + fn Gpu(&self) -> DomRoot<GPU> { + self.gpu.or_init(|| GPU::new(&self.global())) + } } diff --git a/components/script/dom/worklet.rs b/components/script/dom/worklet.rs index 398710ee558..196864f5527 100644 --- a/components/script/dom/worklet.rs +++ b/components/script/dom/worklet.rs @@ -417,7 +417,7 @@ struct WorkletThreadInit { } /// A thread for executing worklets. -#[must_root] +#[unrooted_must_root_lint::must_root] struct WorkletThread { /// Which role the thread is currently playing role: WorkletThreadRole, @@ -477,7 +477,7 @@ impl WorkletThread { global_init: init.global_init, global_scopes: HashMap::new(), control_buffer: None, - runtime: new_rt_and_cx(), + runtime: new_rt_and_cx(None), should_gc: false, gc_threshold: MIN_GC_THRESHOLD, }); diff --git a/components/script/dom/workletglobalscope.rs b/components/script/dom/workletglobalscope.rs index 90fa5bd0115..995014cfc1a 100644 --- a/components/script/dom/workletglobalscope.rs +++ b/components/script/dom/workletglobalscope.rs @@ -15,7 +15,6 @@ use crate::script_thread::MainThreadScriptMsg; use crossbeam_channel::Sender; use devtools_traits::ScriptToDevtoolsControlMsg; use dom_struct::dom_struct; -use ipc_channel::ipc; use ipc_channel::ipc::IpcSender; use js::jsval::UndefinedValue; use js::rust::Runtime; @@ -55,8 +54,6 @@ impl WorkletGlobalScope { executor: WorkletExecutor, init: &WorkletGlobalScopeInit, ) -> Self { - // Any timer events fired on this global are ignored. - let (timer_event_chan, _) = ipc::channel().unwrap(); let script_to_constellation_chan = ScriptToConstellationChan { sender: init.to_constellation_sender.clone(), pipeline_id, @@ -70,7 +67,6 @@ impl WorkletGlobalScope { script_to_constellation_chan, init.scheduler_chan.clone(), init.resource_threads.clone(), - timer_event_chan, MutableOrigin::new(ImmutableOrigin::new_opaque()), Default::default(), init.is_headless, diff --git a/components/script/dom/xrsession.rs b/components/script/dom/xrsession.rs index 6fbdc592b0e..438705853f6 100644 --- a/components/script/dom/xrsession.rs +++ b/components/script/dom/xrsession.rs @@ -47,7 +47,8 @@ use std::cell::Cell; use std::mem; use std::rc::Rc; use webxr_api::{ - self, EnvironmentBlendMode, Event as XREvent, Frame, SelectEvent, Session, Visibility, + self, EnvironmentBlendMode, Event as XREvent, Frame, SelectEvent, SelectKind, Session, + Visibility, }; #[dom_struct] @@ -218,16 +219,22 @@ impl XRSession { let event = XRSessionEvent::new(&self.global(), atom!("end"), false, false, self); event.upcast::<Event>().fire(self.upcast()); }, - XREvent::Select(input, kind, frame) => { + XREvent::Select(input, kind, ty, frame) => { + use servo_atoms::Atom; + const START_ATOMS: [Atom; 2] = [atom!("selectstart"), atom!("squeezestart")]; + const EVENT_ATOMS: [Atom; 2] = [atom!("select"), atom!("squeeze")]; + const END_ATOMS: [Atom; 2] = [atom!("selectend"), atom!("squeezeend")]; + // https://immersive-web.github.io/webxr/#primary-action let source = self.input_sources.find(input); + let atom_index = if kind == SelectKind::Squeeze { 1 } else { 0 }; if let Some(source) = source { let frame = XRFrame::new(&self.global(), self, frame); frame.set_active(true); - if kind == SelectEvent::Start { + if ty == SelectEvent::Start { let event = XRInputSourceEvent::new( &self.global(), - atom!("selectstart"), + START_ATOMS[atom_index].clone(), false, false, &frame, @@ -235,10 +242,10 @@ impl XRSession { ); event.upcast::<Event>().fire(self.upcast()); } else { - if kind == SelectEvent::Select { + if ty == SelectEvent::Select { let event = XRInputSourceEvent::new( &self.global(), - atom!("select"), + EVENT_ATOMS[atom_index].clone(), false, false, &frame, @@ -248,7 +255,7 @@ impl XRSession { } let event = XRInputSourceEvent::new( &self.global(), - atom!("selectend"), + END_ATOMS[atom_index].clone(), false, false, &frame, @@ -353,6 +360,15 @@ impl XRSessionMethods for XRSession { /// https://immersive-web.github.io/webxr/#eventdef-xrsession-selectend event_handler!(selectend, GetOnselectend, SetOnselectend); + /// https://immersive-web.github.io/webxr/#eventdef-xrsession-squeeze + event_handler!(squeeze, GetOnsqueeze, SetOnsqueeze); + + /// https://immersive-web.github.io/webxr/#eventdef-xrsession-squeezestart + event_handler!(squeezestart, GetOnsqueezestart, SetOnsqueezestart); + + /// https://immersive-web.github.io/webxr/#eventdef-xrsession-squeezeend + event_handler!(squeezeend, GetOnsqueezeend, SetOnsqueezeend); + /// https://immersive-web.github.io/webxr/#eventdef-xrsession-visibilitychange event_handler!( visibilitychange, diff --git a/components/script/euclidext.rs b/components/script/euclidext.rs new file mode 100644 index 00000000000..39de2aa2ccb --- /dev/null +++ b/components/script/euclidext.rs @@ -0,0 +1,43 @@ +/* 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 euclid::default::{Rect, Size2D}; + +pub trait Size2DExt { + fn to_u64(&self) -> Size2D<u64>; +} + +impl Size2DExt for Size2D<f32> { + fn to_u64(&self) -> Size2D<u64> { + self.cast() + } +} + +impl Size2DExt for Size2D<f64> { + fn to_u64(&self) -> Size2D<u64> { + self.cast() + } +} + +impl Size2DExt for Size2D<u32> { + fn to_u64(&self) -> Size2D<u64> { + self.cast() + } +} + +pub trait RectExt { + fn to_u64(&self) -> Rect<u64>; +} + +impl RectExt for Rect<f64> { + fn to_u64(&self) -> Rect<u64> { + self.cast() + } +} + +impl RectExt for Rect<u32> { + fn to_u64(&self) -> Rect<u64> { + self.cast() + } +} diff --git a/components/script/init.rs b/components/script/init.rs index 15428f4f3fe..30e01dda74e 100644 --- a/components/script/init.rs +++ b/components/script/init.rs @@ -4,6 +4,7 @@ use crate::dom::bindings::codegen::RegisterBindings; use crate::dom::bindings::proxyhandler; +use crate::script_runtime::JSEngineSetup; use crate::serviceworker_manager::ServiceWorkerManager; use script_traits::SWManagerSenders; @@ -56,7 +57,7 @@ pub fn init_service_workers(sw_senders: SWManagerSenders) { } #[allow(unsafe_code)] -pub fn init() { +pub fn init() -> JSEngineSetup { unsafe { proxyhandler::init(); @@ -66,4 +67,6 @@ pub fn init() { } perform_platform_specific_initialization(); + + JSEngineSetup::new() } diff --git a/components/script/lib.rs b/components/script/lib.rs index 48f42cd7a38..79c0657b996 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -6,14 +6,15 @@ #![feature(core_intrinsics)] #![feature(drain_filter)] #![feature(inner_deref)] -#![feature(on_unimplemented)] #![feature(plugin)] +#![feature(register_tool)] #![deny(unsafe_code)] #![allow(non_snake_case)] #![doc = "The script crate contains all matters DOM."] #![cfg_attr(not(feature = "unrooted_must_root_lint"), allow(unknown_lints))] #![allow(deprecated)] // FIXME: Can we make `allow` only apply to the `plugin` crate attribute? #![plugin(script_plugins)] +#![register_tool(unrooted_must_root_lint)] #[macro_use] extern crate bitflags; @@ -66,6 +67,7 @@ mod dom; mod canvas_state; #[warn(deprecated)] mod compartments; +mod euclidext; #[warn(deprecated)] pub mod fetch; #[warn(deprecated)] @@ -111,6 +113,7 @@ mod unpremultiplytable; mod webdriver_handlers; pub use init::{init, init_service_workers}; +pub use script_runtime::JSEngineSetup; /// A module with everything layout can use from script. /// diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index 10198050ace..fa02e7e564e 100644 --- a/components/script/script_runtime.rs +++ b/components/script/script_runtime.rs @@ -7,9 +7,14 @@ #![allow(dead_code)] +use crate::body::BodyOperations; use crate::dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback; +use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseBinding::ResponseMethods; +use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType; use crate::dom::bindings::conversions::get_dom_class; use crate::dom::bindings::conversions::private_from_object; +use crate::dom::bindings::conversions::root_from_handleobject; +use crate::dom::bindings::error::{throw_dom_exception, Error}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::refcounted::{trace_refcounted_objects, LiveDOMReferences}; use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; @@ -23,16 +28,24 @@ use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::promise::Promise; use crate::dom::promiserejectionevent::PromiseRejectionEvent; +use crate::dom::response::Response; use crate::microtask::{EnqueuedPromiseCallback, Microtask, MicrotaskQueue}; use crate::script_thread::trace_thread; use crate::task::TaskBox; +use crate::task_source::networking::NetworkingTaskSource; use crate::task_source::{TaskSource, TaskSourceName}; -use js::glue::{CollectServoSizes, CreateJobQueue, DeleteJobQueue, JobQueueTraps, SetBuildId}; -use js::glue::{RUST_js_GetErrorMessage, StreamConsumerConsumeChunk, StreamConsumerStreamEnd}; -use js::glue::{StreamConsumerNoteResponseURLs, StreamConsumerStreamError}; +use js::glue::{CollectServoSizes, CreateJobQueue, DeleteJobQueue, DispatchableRun}; +use js::glue::{JobQueueTraps, RUST_js_GetErrorMessage, SetBuildId, StreamConsumerConsumeChunk}; +use js::glue::{ + StreamConsumerNoteResponseURLs, StreamConsumerStreamEnd, StreamConsumerStreamError, +}; use js::jsapi::ContextOptionsRef; +use js::jsapi::InitConsumeStreamCallback; +use js::jsapi::InitDispatchToEventLoop; +use js::jsapi::MimeType; use js::jsapi::StreamConsumer as JSStreamConsumer; use js::jsapi::{BuildIdCharVector, DisableIncrementalGC, GCDescription, GCProgress}; +use js::jsapi::{Dispatchable as JSRunnable, Dispatchable_MaybeShuttingDown}; use js::jsapi::{HandleObject, Heap, JobQueue}; use js::jsapi::{JSContext as RawJSContext, JSTracer, SetDOMCallbacks, SetGCSliceCallback}; use js::jsapi::{JSGCInvocationKind, JSGCStatus, JS_AddExtraGCRootsTracer, JS_SetGCCallback}; @@ -46,10 +59,11 @@ use js::jsval::UndefinedValue; use js::panic::wrap_panic; use js::rust::wrappers::{GetPromiseIsHandled, JS_GetPromiseResult}; use js::rust::Handle; +use js::rust::HandleObject as RustHandleObject; use js::rust::IntoHandle; -use js::rust::JSEngine; use js::rust::ParentRuntime; use js::rust::Runtime as RustRuntime; +use js::rust::{JSEngine, JSEngineHandle}; use malloc_size_of::MallocSizeOfOps; use msg::constellation_msg::PipelineId; use profile_traits::mem::{Report, ReportKind, ReportsChan}; @@ -65,7 +79,9 @@ use std::os::raw::c_void; use std::panic::AssertUnwindSafe; use std::ptr; use std::rc::Rc; -use std::sync::Arc; +use std::sync::Mutex; +use std::thread; +use std::time::Duration; use style::thread_state::{self, ThreadState}; use time::{now, Tm}; @@ -381,27 +397,53 @@ impl Deref for Runtime { } } +pub struct JSEngineSetup(JSEngine); + +impl JSEngineSetup { + pub fn new() -> Self { + let engine = JSEngine::init().unwrap(); + *JS_ENGINE.lock().unwrap() = Some(engine.handle()); + Self(engine) + } +} + +impl Drop for JSEngineSetup { + fn drop(&mut self) { + *JS_ENGINE.lock().unwrap() = None; + + while !self.0.can_shutdown() { + thread::sleep(Duration::from_millis(50)); + } + } +} + lazy_static! { - static ref JS_ENGINE: Arc<JSEngine> = JSEngine::init().unwrap(); + static ref JS_ENGINE: Mutex<Option<JSEngineHandle>> = Mutex::new(None); } #[allow(unsafe_code)] -pub unsafe fn new_child_runtime(parent: ParentRuntime) -> Runtime { - new_rt_and_cx_with_parent(Some(parent)) +pub unsafe fn new_child_runtime( + parent: ParentRuntime, + networking_task_source: Option<NetworkingTaskSource>, +) -> Runtime { + new_rt_and_cx_with_parent(Some(parent), networking_task_source) } #[allow(unsafe_code)] -pub fn new_rt_and_cx() -> Runtime { - unsafe { new_rt_and_cx_with_parent(None) } +pub fn new_rt_and_cx(networking_task_source: Option<NetworkingTaskSource>) -> Runtime { + unsafe { new_rt_and_cx_with_parent(None, networking_task_source) } } #[allow(unsafe_code)] -unsafe fn new_rt_and_cx_with_parent(parent: Option<ParentRuntime>) -> Runtime { +unsafe fn new_rt_and_cx_with_parent( + parent: Option<ParentRuntime>, + networking_task_source: Option<NetworkingTaskSource>, +) -> Runtime { LiveDOMReferences::initialize(); let runtime = if let Some(parent) = parent { RustRuntime::create_with_parent(parent) } else { - RustRuntime::new(JS_ENGINE.clone()) + RustRuntime::new(JS_ENGINE.lock().unwrap().as_ref().unwrap().clone()) }; let cx = runtime.cx(); @@ -424,6 +466,30 @@ unsafe fn new_rt_and_cx_with_parent(parent: Option<ParentRuntime>) -> Runtime { // Pre barriers aren't working correctly at the moment DisableIncrementalGC(cx); + unsafe extern "C" fn dispatch_to_event_loop( + closure: *mut c_void, + dispatchable: *mut JSRunnable, + ) -> bool { + let networking_task_src: &NetworkingTaskSource = &*(closure as *mut NetworkingTaskSource); + let runnable = Runnable(dispatchable); + let task = task!(dispatch_to_event_loop_message: move || { + runnable.run(RustRuntime::get(), Dispatchable_MaybeShuttingDown::NotShuttingDown); + }); + + networking_task_src.queue_unconditionally(task).is_ok() + } + + if let Some(source) = networking_task_source { + let networking_task_src = Box::new(source); + InitDispatchToEventLoop( + cx, + Some(dispatch_to_event_loop), + Box::into_raw(networking_task_src) as *mut c_void, + ); + } + + InitConsumeStreamCallback(cx, Some(consume_stream), Some(report_stream_error)); + let microtask_queue = Rc::new(MicrotaskQueue::default()); let job_queue = CreateJobQueue( &JOB_QUEUE_TRAPS, @@ -750,7 +816,7 @@ unsafe fn set_gc_zeal_options(cx: *mut RawJSContext) { use js::jsapi::{JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ}; let level = match pref!(js.mem.gc.zeal.level) { - level @ 0...14 => level as u8, + level @ 0..=14 => level as u8, _ => return, }; let frequency = match pref!(js.mem.gc.zeal.frequency) { @@ -825,10 +891,112 @@ impl StreamConsumer { } } +/// Implements the steps to compile webassembly response mentioned here +/// <https://webassembly.github.io/spec/web-api/#compile-a-potential-webassembly-response> +#[allow(unsafe_code)] +unsafe extern "C" fn consume_stream( + _cx: *mut RawJSContext, + obj: HandleObject, + _mimeType: MimeType, + _consumer: *mut JSStreamConsumer, +) -> bool { + let cx = JSContext::from_ptr(_cx); + let global = GlobalScope::from_context(*cx); + + //Step 2.1 Upon fulfillment of source, store the Response with value unwrappedSource. + if let Ok(unwrapped_source) = + root_from_handleobject::<Response>(RustHandleObject::from_raw(obj), *cx) + { + //Step 2.2 Let mimeType be the result of extracting a MIME type from response’s header list. + let mimetype = unwrapped_source.Headers().extract_mime_type(); + + //Step 2.3 If mimeType is not `application/wasm`, return with a TypeError and abort these substeps. + match &mimetype[..] { + b"application/wasm" | b"APPLICATION/wasm" | b"APPLICATION/WASM" => {}, + _ => { + throw_dom_exception( + cx, + &global, + Error::Type("Response has unsupported MIME type".to_string()), + ); + return false; + }, + } + + //Step 2.4 If response is not CORS-same-origin, return with a TypeError and abort these substeps. + match unwrapped_source.Type() { + DOMResponseType::Basic | DOMResponseType::Cors | DOMResponseType::Default => {}, + _ => { + throw_dom_exception( + cx, + &global, + Error::Type("Response.type must be 'basic', 'cors' or 'default'".to_string()), + ); + return false; + }, + } + + //Step 2.5 If response’s status is not an ok status, return with a TypeError and abort these substeps. + if !unwrapped_source.Ok() { + throw_dom_exception( + cx, + &global, + Error::Type("Response does not have ok status".to_string()), + ); + return false; + } + + // Step 2.6.1 If response body is locked, return with a TypeError and abort these substeps. + if unwrapped_source.is_locked() { + throw_dom_exception( + cx, + &global, + Error::Type("There was an error consuming the Response".to_string()), + ); + return false; + } + + // Step 2.6.2 If response body is alreaady consumed, return with a TypeError and abort these substeps. + if unwrapped_source.get_body_used() { + throw_dom_exception( + cx, + &global, + Error::Type("Response already consumed".to_string()), + ); + return false; + } + } else { + //Step 3 Upon rejection of source, return with reason. + throw_dom_exception( + cx, + &global, + Error::Type("expected Response or Promise resolving to Response".to_string()), + ); + return false; + } + return true; +} + #[allow(unsafe_code)] -unsafe extern "C" fn report_stream_error_callback(_cx: *mut JSContext, error_code: usize) { +unsafe extern "C" fn report_stream_error(_cx: *mut RawJSContext, error_code: usize) { error!( "Error initializing StreamConsumer: {:?}", RUST_js_GetErrorMessage(ptr::null_mut(), error_code as u32) ); } + +pub struct Runnable(*mut JSRunnable); + +#[allow(unsafe_code)] +unsafe impl Sync for Runnable {} +#[allow(unsafe_code)] +unsafe impl Send for Runnable {} + +#[allow(unsafe_code)] +impl Runnable { + fn run(&self, cx: *mut RawJSContext, maybe_shutting_down: Dispatchable_MaybeShuttingDown) { + unsafe { + DispatchableRun(cx, self.0, maybe_shutting_down); + } + } +} diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 6a7a69651e7..c01ad08220c 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -82,6 +82,7 @@ use crate::task_source::networking::NetworkingTaskSource; use crate::task_source::performance_timeline::PerformanceTimelineTaskSource; use crate::task_source::port_message::PortMessageQueue; use crate::task_source::remote_event::RemoteEventTaskSource; +use crate::task_source::timer::TimerTaskSource; use crate::task_source::user_interaction::UserInteractionTaskSource; use crate::task_source::websocket::WebsocketTaskSource; use crate::task_source::TaskSource; @@ -138,11 +139,11 @@ use script_traits::{ DiscardBrowsingContext, DocumentActivity, EventResult, HistoryEntryReplacement, }; use script_traits::{InitialScriptState, JsEvalResult, LayoutMsg, LoadData, LoadOrigin}; -use script_traits::{MouseButton, MouseEventType, NewLayoutInfo}; +use script_traits::{MediaSessionActionType, MouseButton, MouseEventType, NewLayoutInfo}; use script_traits::{Painter, ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory}; -use script_traits::{ScriptToConstellationChan, TimerEvent, TimerSchedulerMsg}; -use script_traits::{TimerSource, TouchEventType, TouchId, UntrustedNodeAddress, WheelDelta}; -use script_traits::{UpdatePipelineIdReason, WindowSizeData, WindowSizeType}; +use script_traits::{ScriptToConstellationChan, TimerSchedulerMsg}; +use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress, WheelDelta}; +use script_traits::{UpdatePipelineIdReason, WebrenderIpcSender, WindowSizeData, WindowSizeType}; use servo_atoms::Atom; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; use std::borrow::Cow; @@ -163,8 +164,9 @@ use style::dom::OpaqueNode; use style::thread_state::{self, ThreadState}; use time::{at_utc, get_time, precise_time_ns, Timespec}; use url::Position; +use webgpu::WebGPU; use webrender_api::units::LayoutPixel; -use webrender_api::{DocumentId, RenderApiSender}; +use webrender_api::DocumentId; use webvr_traits::{WebVREvent, WebVRMsg}; pub type ImageCacheMsg = (PipelineId, PendingImageResponse); @@ -263,7 +265,6 @@ enum MixedMessage { FromScript(MainThreadScriptMsg), FromDevtools(DevtoolScriptControlMsg), FromImageCache((PipelineId, PendingImageResponse)), - FromScheduler(TimerEvent), } /// Messages used to control the script event loop. @@ -434,7 +435,7 @@ impl OpaqueSender<CommonScriptMsg> for Sender<MainThreadScriptMsg> { /// The set of all documents managed by this script thread. #[derive(JSTraceable)] -#[must_root] +#[unrooted_must_root_lint::must_root] pub struct Documents { map: HashMap<PipelineId, Dom<Document>>, } @@ -569,6 +570,8 @@ pub struct ScriptThread { port_message_sender: Box<dyn ScriptChan>, + timer_task_sender: Box<dyn ScriptChan>, + remote_event_task_sender: Box<dyn ScriptChan>, /// A channel to hand out to threads that need to respond to a message from the script thread. @@ -612,8 +615,6 @@ pub struct ScriptThread { closed_pipelines: DomRefCell<HashSet<PipelineId>>, scheduler_chan: IpcSender<TimerSchedulerMsg>, - timer_event_chan: Sender<TimerEvent>, - timer_event_port: Receiver<TimerEvent>, content_process_shutdown_chan: Sender<()>, @@ -629,6 +630,9 @@ pub struct ScriptThread { /// A handle to the WebGL thread webgl_chan: Option<WebGLPipeline>, + /// A handle to the WebGPU threads + webgpu: Option<WebGPU>, + /// A handle to the webvr thread, if available webvr_chan: Option<IpcSender<WebVRMsg>>, @@ -653,7 +657,7 @@ pub struct ScriptThread { webrender_document: DocumentId, /// Webrender API sender. - webrender_api_sender: RenderApiSender, + webrender_api_sender: WebrenderIpcSender, /// Periodically print out on which events script threads spend their processing time. profile_script_events: bool, @@ -1244,7 +1248,12 @@ impl ScriptThread { replace_surrogates: bool, user_agent: Cow<'static, str>, ) -> ScriptThread { - let runtime = new_rt_and_cx(); + let boxed_script_sender = Box::new(MainThreadScriptChan(chan.clone())); + + let runtime = new_rt_and_cx(Some(NetworkingTaskSource( + boxed_script_sender.clone(), + state.id, + ))); let cx = runtime.cx(); unsafe { @@ -1257,13 +1266,9 @@ impl ScriptThread { let devtools_port = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(ipc_devtools_receiver); - let (timer_event_chan, timer_event_port) = unbounded(); - // Ask the router to proxy IPC messages from the control port to us. let control_port = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(state.control_port); - let boxed_script_sender = Box::new(MainThreadScriptChan(chan.clone())); - let (image_cache_channel, image_cache_port) = unbounded(); let task_queue = TaskQueue::new(port, chan.clone()); @@ -1302,6 +1307,7 @@ impl ScriptThread { port_message_sender: boxed_script_sender.clone(), file_reading_task_sender: boxed_script_sender.clone(), performance_timeline_task_sender: boxed_script_sender.clone(), + timer_task_sender: boxed_script_sender.clone(), remote_event_task_sender: boxed_script_sender.clone(), history_traversal_task_sender: chan.clone(), @@ -1323,8 +1329,6 @@ impl ScriptThread { closed_pipelines: DomRefCell::new(HashSet::new()), scheduler_chan: state.scheduler_chan, - timer_event_chan: timer_event_chan, - timer_event_port: timer_event_port, content_process_shutdown_chan: state.content_process_shutdown_chan, @@ -1335,6 +1339,7 @@ impl ScriptThread { layout_to_constellation_chan: state.layout_to_constellation_chan, webgl_chan: state.webgl_chan, + webgpu: state.webgpu, webvr_chan: state.webvr_chan, webxr_registry: state.webxr_registry, @@ -1386,8 +1391,8 @@ impl ScriptThread { /// Handle incoming control messages. fn handle_msgs(&self) -> bool { + use self::MixedMessage::FromScript; use self::MixedMessage::{FromConstellation, FromDevtools, FromImageCache}; - use self::MixedMessage::{FromScheduler, FromScript}; // Handle pending resize events. // Gather them first to avoid a double mut borrow on self. @@ -1422,7 +1427,6 @@ impl ScriptThread { FromScript(event) }, recv(self.control_port) -> msg => FromConstellation(msg.unwrap()), - recv(self.timer_event_port) -> msg => FromScheduler(msg.unwrap()), recv(self.devtools_chan.as_ref().map(|_| &self.devtools_port).unwrap_or(&crossbeam_channel::never())) -> msg => FromDevtools(msg.unwrap()), recv(self.image_cache_port) -> msg => FromImageCache(msg.unwrap()), @@ -1520,15 +1524,12 @@ impl ScriptThread { // on and execute the sequential non-resize events we've seen. match self.control_port.try_recv() { Err(_) => match self.task_queue.try_recv() { - Err(_) => match self.timer_event_port.try_recv() { - Err(_) => match self.devtools_port.try_recv() { - Err(_) => match self.image_cache_port.try_recv() { - Err(_) => break, - Ok(ev) => event = FromImageCache(ev), - }, - Ok(ev) => event = FromDevtools(ev), + Err(_) => match self.devtools_port.try_recv() { + Err(_) => match self.image_cache_port.try_recv() { + Err(_) => break, + Ok(ev) => event = FromImageCache(ev), }, - Ok(ev) => event = FromScheduler(ev), + Ok(ev) => event = FromDevtools(ev), }, Ok(ev) => event = FromScript(ev), }, @@ -1552,7 +1553,6 @@ impl ScriptThread { }, FromConstellation(inner_msg) => self.handle_msg_from_constellation(inner_msg), FromScript(inner_msg) => self.handle_msg_from_script(inner_msg), - FromScheduler(inner_msg) => self.handle_timer_event(inner_msg), FromDevtools(inner_msg) => self.handle_msg_from_devtools(inner_msg), FromImageCache(inner_msg) => self.handle_msg_from_image_cache(inner_msg), } @@ -1625,7 +1625,6 @@ impl ScriptThread { }, _ => ScriptThreadEventCategory::ScriptEvent, }, - MixedMessage::FromScheduler(_) => ScriptThreadEventCategory::TimerEvent, } } @@ -1713,6 +1712,7 @@ impl ScriptThread { WebVREvents(id, ..) => Some(id), PaintMetric(..) => None, ExitFullScreen(id, ..) => Some(id), + MediaSessionAction(..) => None, } }, MixedMessage::FromDevtools(_) => None, @@ -1728,13 +1728,6 @@ impl ScriptThread { MainThreadScriptMsg::WakeUp => None, }, MixedMessage::FromImageCache((pipeline_id, _)) => Some(pipeline_id), - MixedMessage::FromScheduler(ref timer_event) => { - let TimerEvent(source, _) = *timer_event; - match source { - TimerSource::FromWindow(pipeline_id) => Some(pipeline_id), - _ => None, - } - }, } } @@ -1942,6 +1935,9 @@ impl ScriptThread { ConstellationControlMsg::PaintMetric(pipeline_id, metric_type, metric_value) => { self.handle_paint_metric(pipeline_id, metric_type, metric_value) }, + ConstellationControlMsg::MediaSessionAction(pipeline_id, action) => { + self.handle_media_session_action(pipeline_id, action) + }, msg @ ConstellationControlMsg::AttachLayout(..) | msg @ ConstellationControlMsg::Viewport(..) | msg @ ConstellationControlMsg::SetScrollState(..) | @@ -1976,36 +1972,6 @@ impl ScriptThread { } } - fn handle_timer_event(&self, timer_event: TimerEvent) { - let TimerEvent(source, id) = timer_event; - - let pipeline_id = match source { - TimerSource::FromWindow(pipeline_id) => pipeline_id, - TimerSource::FromWorker => panic!("Worker timeouts must not be sent to script thread"), - }; - - let window = self.documents.borrow().find_window(pipeline_id); - let window = match window { - Some(w) => { - if w.Closed() { - return warn!( - "Received fire timer msg for a discarded browsing context whose pipeline is pending exit {}.", - pipeline_id - ); - } - w - }, - None => { - return warn!( - "Received fire timer msg for a closed pipeline {}.", - pipeline_id - ); - }, - }; - - window.handle_fire_timer(id); - } - fn handle_msg_from_devtools(&self, msg: DevtoolScriptControlMsg) { let documents = self.documents.borrow(); match msg { @@ -2392,6 +2358,7 @@ impl ScriptThread { load_data.url.clone(), ), layout_is_busy: layout_is_busy.clone(), + window_size, }); // Pick a layout thread, any layout thread @@ -2429,6 +2396,8 @@ impl ScriptThread { ); if load_data.url.as_str() == "about:blank" { self.start_page_load_about_blank(new_load, load_data.js_eval_result); + } else if load_data.url.as_str() == "about:srcdoc" { + self.page_load_about_srcdoc(new_load, load_data.srcdoc); } else { self.pre_page_load(new_load, load_data); } @@ -2535,7 +2504,8 @@ impl ScriptThread { source_origin: ImmutableOrigin, data: StructuredSerializedData, ) { - match { self.documents.borrow().find_window(pipeline_id) } { + let window = self.documents.borrow().find_window(pipeline_id); + match window { None => return warn!("postMessage after target pipeline {} closed.", pipeline_id), Some(window) => { // FIXME: synchronously talks to constellation. @@ -2618,7 +2588,8 @@ impl ScriptThread { history_state_id: Option<HistoryStateId>, url: ServoUrl, ) { - match { self.documents.borrow().find_window(pipeline_id) } { + let window = self.documents.borrow().find_window(pipeline_id); + match window { None => { return warn!( "update history state after pipeline {} closed.", @@ -2634,7 +2605,8 @@ impl ScriptThread { pipeline_id: PipelineId, history_states: Vec<HistoryStateId>, ) { - match { self.documents.borrow().find_window(pipeline_id) } { + let window = self.documents.borrow().find_window(pipeline_id); + match window { None => { return warn!( "update history state after pipeline {} closed.", @@ -2806,6 +2778,10 @@ impl ScriptThread { RemoteEventTaskSource(self.remote_event_task_sender.clone(), pipeline_id) } + pub fn timer_task_source(&self, pipeline_id: PipelineId) -> TimerTaskSource { + TimerTaskSource(self.timer_task_sender.clone(), pipeline_id) + } + pub fn websocket_task_source(&self, pipeline_id: PipelineId) -> WebsocketTaskSource { WebsocketTaskSource(self.remote_event_task_sender.clone(), pipeline_id) } @@ -2834,6 +2810,14 @@ impl ScriptThread { let document = self.documents.borrow_mut().remove(id); + // Abort the parser, if any, + // to prevent any further incoming networking messages from being handled. + if let Some(document) = document.as_ref() { + if let Some(parser) = document.get_current_parser() { + parser.abort(); + } + } + // We should never have a pipeline that's still an incomplete load, // but also has a Document. debug_assert!(idx.is_none() || document.is_none()); @@ -3112,7 +3096,8 @@ impl ScriptThread { opener: Option<BrowsingContextId>, ) -> DomRoot<WindowProxy> { if let Some(window_proxy) = self.window_proxies.borrow().get(&browsing_context_id) { - window_proxy.set_currently_active(&*window); + // Note: we do not set the window to be the currently-active one, + // this will be done instead when the script-thread handles the `SetDocumentActivity` msg. return DomRoot::from_ref(window_proxy); } let iframe = parent_info.and_then(|parent_id| { @@ -3170,13 +3155,8 @@ impl ScriptThread { let MainThreadScriptChan(ref sender) = self.chan; - let (ipc_timer_event_chan, ipc_timer_event_port) = ipc::channel().unwrap(); - ROUTER.route_ipc_receiver_to_crossbeam_sender( - ipc_timer_event_port, - self.timer_event_chan.clone(), - ); - - let origin = if final_url.as_str() == "about:blank" { + let origin = if final_url.as_str() == "about:blank" || final_url.as_str() == "about:srcdoc" + { incomplete.origin.clone() } else { MutableOrigin::new(final_url.origin()) @@ -3198,6 +3178,7 @@ impl ScriptThread { self.port_message_queue(incomplete.pipeline_id), self.user_interaction_task_source(incomplete.pipeline_id), self.remote_event_task_source(incomplete.pipeline_id), + self.timer_task_source(incomplete.pipeline_id), self.websocket_task_source(incomplete.pipeline_id), ); // Create the window and document objects. @@ -3215,7 +3196,6 @@ impl ScriptThread { script_to_constellation_chan, self.control_chan.clone(), self.scheduler_chan.clone(), - ipc_timer_event_chan, incomplete.layout_chan, incomplete.pipeline_id, incomplete.parent_info, @@ -3224,6 +3204,7 @@ impl ScriptThread { incomplete.navigation_start, incomplete.navigation_start_precise, self.webgl_chan.as_ref().map(|chan| chan.channel()), + self.webgpu.clone(), self.webvr_chan.clone(), self.webxr_registry.clone(), self.microtask_queue.clone(), @@ -3696,6 +3677,15 @@ impl ScriptThread { }; let window = document.window(); + if window.window_size() == new_size { + return; + } + debug!( + "resizing pipeline {:?} from {:?} to {:?}", + pipeline_id, + window.window_size(), + new_size + ); window.set_window_size(new_size); window.force_reflow(ReflowGoal::Full, ReflowReason::WindowResize); @@ -3828,6 +3818,25 @@ impl ScriptThread { context.process_response_eof(Ok(ResourceFetchTiming::new(ResourceTimingType::None))); } + /// Synchronously parse a srcdoc document from a giving HTML string. + fn page_load_about_srcdoc(&self, incomplete: InProgressLoad, src_doc: String) { + let id = incomplete.pipeline_id; + + self.incomplete_loads.borrow_mut().push(incomplete); + + let url = ServoUrl::parse("about:srcdoc").unwrap(); + let mut context = ParserContext::new(id, url.clone()); + + let mut meta = Metadata::default(url); + meta.set_content_type(Some(&mime::TEXT_HTML)); + + let chunk = src_doc.into_bytes(); + + context.process_response(Ok(FetchMetadata::Unfiltered(meta))); + context.process_response_chunk(chunk); + context.process_response_eof(Ok(ResourceFetchTiming::new(ResourceTimingType::None))); + } + fn handle_css_error_reporting( &self, pipeline_id: PipelineId, @@ -3890,6 +3899,15 @@ impl ScriptThread { } } + fn handle_media_session_action(&self, pipeline_id: PipelineId, action: MediaSessionActionType) { + if let Some(window) = self.documents.borrow().find_window(pipeline_id) { + let media_session = window.Navigator().MediaSession(); + media_session.handle_action(action); + } else { + warn!("No MediaSession for this pipeline ID"); + }; + } + pub fn enqueue_microtask(job: Microtask) { SCRIPT_THREAD_ROOT.with(|root| { let script_thread = unsafe { &*root.get().unwrap() }; diff --git a/components/script/serviceworkerjob.rs b/components/script/serviceworkerjob.rs index 8ba955928a3..93340cabe27 100644 --- a/components/script/serviceworkerjob.rs +++ b/components/script/serviceworkerjob.rs @@ -38,7 +38,7 @@ pub enum SettleType { Reject(Error), } -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(JSTraceable)] pub struct Job { pub job_type: JobType, @@ -97,7 +97,7 @@ impl PartialEq for Job { } } -#[must_root] +#[unrooted_must_root_lint::must_root] #[derive(JSTraceable)] pub struct JobQueue(pub DomRefCell<HashMap<ServoUrl, Vec<Job>>>); diff --git a/components/script/task_manager.rs b/components/script/task_manager.rs index a8908ea00a2..063b5700484 100644 --- a/components/script/task_manager.rs +++ b/components/script/task_manager.rs @@ -12,6 +12,7 @@ use crate::task_source::networking::NetworkingTaskSource; use crate::task_source::performance_timeline::PerformanceTimelineTaskSource; use crate::task_source::port_message::PortMessageQueue; use crate::task_source::remote_event::RemoteEventTaskSource; +use crate::task_source::timer::TimerTaskSource; use crate::task_source::user_interaction::UserInteractionTaskSource; use crate::task_source::websocket::WebsocketTaskSource; use crate::task_source::TaskSourceName; @@ -54,6 +55,8 @@ pub struct TaskManager { #[ignore_malloc_size_of = "task sources are hard"] remote_event_task_source: RemoteEventTaskSource, #[ignore_malloc_size_of = "task sources are hard"] + timer_task_source: TimerTaskSource, + #[ignore_malloc_size_of = "task sources are hard"] websocket_task_source: WebsocketTaskSource, } @@ -68,6 +71,7 @@ impl TaskManager { port_message_queue: PortMessageQueue, user_interaction_task_source: UserInteractionTaskSource, remote_event_task_source: RemoteEventTaskSource, + timer_task_source: TimerTaskSource, websocket_task_source: WebsocketTaskSource, ) -> Self { TaskManager { @@ -80,6 +84,7 @@ impl TaskManager { port_message_queue, user_interaction_task_source, remote_event_task_source, + timer_task_source, websocket_task_source, task_cancellers: Default::default(), } @@ -159,6 +164,14 @@ impl TaskManager { task_source_functions!( self, + timer_task_source_with_canceller, + timer_task_source, + TimerTaskSource, + Timer + ); + + task_source_functions!( + self, websocket_task_source_with_canceller, websocket_task_source, WebsocketTaskSource, diff --git a/components/script/task_source/mod.rs b/components/script/task_source/mod.rs index d315b34fcae..7defd922779 100644 --- a/components/script/task_source/mod.rs +++ b/components/script/task_source/mod.rs @@ -10,6 +10,7 @@ pub mod networking; pub mod performance_timeline; pub mod port_message; pub mod remote_event; +pub mod timer; pub mod user_interaction; pub mod websocket; @@ -34,6 +35,7 @@ pub enum TaskSourceName { RemoteEvent, MediaElement, Websocket, + Timer, } impl TaskSourceName { diff --git a/components/script/task_source/timer.rs b/components/script/task_source/timer.rs new file mode 100644 index 00000000000..cd134fb12a2 --- /dev/null +++ b/components/script/task_source/timer.rs @@ -0,0 +1,42 @@ +/* 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::script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory}; +use crate::task::{TaskCanceller, TaskOnce}; +use crate::task_source::{TaskSource, TaskSourceName}; +use msg::constellation_msg::PipelineId; +use std::fmt; + +#[derive(JSTraceable)] +/// https://html.spec.whatwg.org/multipage/#timer-task-source +pub struct TimerTaskSource(pub Box<dyn ScriptChan + Send + 'static>, pub PipelineId); + +impl Clone for TimerTaskSource { + fn clone(&self) -> TimerTaskSource { + TimerTaskSource(self.0.clone(), self.1.clone()) + } +} + +impl fmt::Debug for TimerTaskSource { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "TimerTaskSource(...)") + } +} + +impl TaskSource for TimerTaskSource { + const NAME: TaskSourceName = TaskSourceName::Timer; + + fn queue_with_canceller<T>(&self, task: T, canceller: &TaskCanceller) -> Result<(), ()> + where + T: TaskOnce + 'static, + { + let msg = CommonScriptMsg::Task( + ScriptThreadEventCategory::TimerEvent, + Box::new(canceller.wrap_task(task)), + Some(self.1), + Self::NAME, + ); + self.0.send(msg).map_err(|_| ()) + } +} diff --git a/components/script/timers.rs b/components/script/timers.rs index f65d1013f48..4be6f822263 100644 --- a/components/script/timers.rs +++ b/components/script/timers.rs @@ -34,8 +34,12 @@ pub struct OneshotTimerHandle(i32); pub struct OneshotTimers { js_timers: JsTimers, #[ignore_malloc_size_of = "Defined in std"] - timer_event_chan: IpcSender<TimerEvent>, + /// The sender, to be cloned for each timer, + /// on which the timer scheduler in the constellation can send an event + /// when the timer is due. + timer_event_chan: DomRefCell<Option<IpcSender<TimerEvent>>>, #[ignore_malloc_size_of = "Defined in std"] + /// The sender to the timer scheduler in the constellation. scheduler_chan: IpcSender<TimerSchedulerMsg>, next_timer_handle: Cell<OneshotTimerHandle>, timers: DomRefCell<Vec<OneshotTimer>>, @@ -109,13 +113,10 @@ impl PartialEq for OneshotTimer { } impl OneshotTimers { - pub fn new( - timer_event_chan: IpcSender<TimerEvent>, - scheduler_chan: IpcSender<TimerSchedulerMsg>, - ) -> OneshotTimers { + pub fn new(scheduler_chan: IpcSender<TimerSchedulerMsg>) -> OneshotTimers { OneshotTimers { js_timers: JsTimers::new(), - timer_event_chan: timer_event_chan, + timer_event_chan: DomRefCell::new(None), scheduler_chan: scheduler_chan, next_timer_handle: Cell::new(OneshotTimerHandle(1)), timers: DomRefCell::new(Vec::new()), @@ -125,6 +126,12 @@ impl OneshotTimers { } } + pub fn setup_scheduling(&self, timer_event_chan: IpcSender<TimerEvent>) { + let mut chan = self.timer_event_chan.borrow_mut(); + assert!(chan.is_none()); + *chan = Some(timer_event_chan); + } + pub fn schedule_callback( &self, callback: OneshotTimerCallback, @@ -279,7 +286,10 @@ impl OneshotTimers { .saturating_sub(precise_time_ms().get()), ); let request = TimerEventRequest( - self.timer_event_chan.clone(), + self.timer_event_chan + .borrow() + .clone() + .expect("Timer event chan not setup to schedule timers."), timer.source, expected_event_id, delay, @@ -331,6 +341,7 @@ pub struct JsTimerHandle(i32); #[derive(DenyPublicFields, JSTraceable, MallocSizeOf)] pub struct JsTimers { next_timer_handle: Cell<JsTimerHandle>, + /// https://html.spec.whatwg.org/multipage/#list-of-active-timers active_timers: DomRefCell<HashMap<JsTimerHandle, JsTimerEntry>>, /// The nesting level of the currently executing timer task or 0. nesting_level: Cell<u32>, |