diff options
Diffstat (limited to 'components/script/dom')
30 files changed, 639 insertions, 487 deletions
diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 38bd38ad511..046d478e49d 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -7,9 +7,9 @@ use dom_struct::dom_struct; use euclid::default::Size2D; use profile_traits::ipc; use script_bindings::inheritance::Castable; -use script_layout_interface::HTMLCanvasDataSource; use servo_url::ServoUrl; use snapshot::Snapshot; +use webrender_api::ImageKey; use crate::canvas_context::{CanvasContext, CanvasHelpers, LayoutCanvasRenderingContextHelpers}; use crate::canvas_state::CanvasState; @@ -98,13 +98,13 @@ impl CanvasRenderingContext2D { } impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, CanvasRenderingContext2D> { - fn canvas_data_source(self) -> HTMLCanvasDataSource { + fn canvas_data_source(self) -> Option<ImageKey> { let canvas_state = &self.unsafe_get().canvas_state; if canvas_state.is_paintable() { - HTMLCanvasDataSource::Image(canvas_state.image_key()) + Some(canvas_state.image_key()) } else { - HTMLCanvasDataSource::Empty + None } } } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 2831fc3d8f0..d92d5c124d1 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -149,7 +149,6 @@ use crate::dom::raredata::ElementRareData; use crate::dom::servoparser::ServoParser; use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot}; use crate::dom::text::Text; -use crate::dom::types::TrustedTypePolicyFactory; use crate::dom::validation::Validatable; use crate::dom::validitystate::ValidationFlags; use crate::dom::virtualmethods::{VirtualMethods, vtable_for}; @@ -1961,35 +1960,6 @@ impl Element { .unwrap_or_else(|_| TrustedScriptURLOrUSVString::USVString(USVString(value.to_owned()))) } - pub(crate) fn set_trusted_type_url_attribute( - &self, - local_name: &LocalName, - value: TrustedScriptURLOrUSVString, - can_gc: CanGc, - ) -> Fallible<()> { - assert_eq!(*local_name, local_name.to_ascii_lowercase()); - let value = match value { - TrustedScriptURLOrUSVString::USVString(url) => { - let global = self.owner_global(); - // TODO(36258): Reflectively get the name of the class - let sink = format!("{} {}", "HTMLScriptElement", &local_name); - let result = TrustedTypePolicyFactory::get_trusted_type_compliant_string( - &global, - url.to_string(), - &sink, - "'script'", - can_gc, - ); - result? - }, - // This partially implements <https://w3c.github.io/trusted-types/dist/spec/#get-trusted-type-compliant-string-algorithm> - // Step 1: If input is an instance of expectedType, return stringified input and abort these steps. - TrustedScriptURLOrUSVString::TrustedScriptURL(script_url) => script_url.to_string(), - }; - self.set_attribute(local_name, AttrValue::String(value), can_gc); - Ok(()) - } - pub(crate) fn get_string_attribute(&self, local_name: &LocalName) -> DOMString { match self.get_attribute(&ns!(), local_name) { Some(x) => x.Value(), @@ -3025,28 +2995,8 @@ impl ElementMethods<crate::DomTypeHolder> for Element { DomRoot::from_ref(self.upcast()) }; - // Step 3. Unsafely set HTML given target, this, and compliantHTML. - // Let newChildren be the result of the HTML fragment parsing algorithm. - let new_children = ServoParser::parse_html_fragment(self, html, true, can_gc); - - let context_document = { - if let Some(template) = self.downcast::<HTMLTemplateElement>() { - template.Content(can_gc).upcast::<Node>().owner_doc() - } else { - self.owner_document() - } - }; - - // Let fragment be a new DocumentFragment whose node document is contextElement's node document. - let frag = DocumentFragment::new(&context_document, can_gc); - - // For each node in newChildren, append node to fragment. - for child in new_children { - frag.upcast::<Node>().AppendChild(&child, can_gc).unwrap(); - } - - // Replace all with fragment within target. - Node::replace_all(Some(frag.upcast()), &target, can_gc); + // Step 3. Unsafely set HTML given target, this, and compliantHTML + Node::unsafely_set_html(&target, self, html, can_gc); } /// <https://html.spec.whatwg.org/multipage/#dom-element-gethtml> diff --git a/components/script/dom/gamepad.rs b/components/script/dom/gamepad.rs index dcafc58bcd9..baf3af7466f 100644 --- a/components/script/dom/gamepad.rs +++ b/components/script/dom/gamepad.rs @@ -13,7 +13,7 @@ use crate::dom::bindings::codegen::Bindings::GamepadBinding::{GamepadHand, Gamep use crate::dom::bindings::codegen::Bindings::GamepadButtonListBinding::GamepadButtonListMethods; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; -use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto}; +use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; use crate::dom::event::Event; @@ -23,6 +23,7 @@ use crate::dom::gamepadevent::{GamepadEvent, GamepadEventType}; use crate::dom::gamepadhapticactuator::GamepadHapticActuator; use crate::dom::gamepadpose::GamepadPose; use crate::dom::globalscope::GlobalScope; +use crate::dom::window::Window; use crate::script_runtime::{CanGc, JSContext}; // This value is for determining when to consider a gamepad as having a user gesture @@ -88,39 +89,14 @@ impl Gamepad { } } - #[allow(clippy::too_many_arguments)] - pub(crate) fn new( - global: &GlobalScope, - gamepad_id: u32, - id: String, - mapping_type: String, - axis_bounds: (f64, f64), - button_bounds: (f64, f64), - supported_haptic_effects: GamepadSupportedHapticEffects, - xr: bool, - can_gc: CanGc, - ) -> DomRoot<Gamepad> { - Self::new_with_proto( - global, - gamepad_id, - id, - mapping_type, - axis_bounds, - button_bounds, - supported_haptic_effects, - xr, - can_gc, - ) - } - /// When we construct a new gamepad, we initialize the number of buttons and /// axes corresponding to the "standard" gamepad mapping. /// The spec says UAs *may* do this for fingerprint mitigation, and it also /// happens to simplify implementation /// <https://www.w3.org/TR/gamepad/#fingerprinting-mitigation> #[allow(clippy::too_many_arguments)] - fn new_with_proto( - global: &GlobalScope, + pub(crate) fn new( + window: &Window, gamepad_id: u32, id: String, mapping_type: String, @@ -130,11 +106,11 @@ impl Gamepad { xr: bool, can_gc: CanGc, ) -> DomRoot<Gamepad> { - let button_list = GamepadButtonList::init_buttons(global, can_gc); + let button_list = GamepadButtonList::init_buttons(window, can_gc); let vibration_actuator = - GamepadHapticActuator::new(global, gamepad_id, supported_haptic_effects, can_gc); + GamepadHapticActuator::new(window, gamepad_id, supported_haptic_effects, can_gc); let index = if xr { -1 } else { 0 }; - let gamepad = reflect_dom_object_with_proto( + let gamepad = reflect_dom_object( Box::new(Gamepad::new_inherited( gamepad_id, id, @@ -149,8 +125,7 @@ impl Gamepad { button_bounds, &vibration_actuator, )), - global, - None, + window, can_gc, ); gamepad.init_axes(can_gc); diff --git a/components/script/dom/gamepadbutton.rs b/components/script/dom/gamepadbutton.rs index fead990ccd3..3588ba775ca 100644 --- a/components/script/dom/gamepadbutton.rs +++ b/components/script/dom/gamepadbutton.rs @@ -10,7 +10,7 @@ use crate::dom::bindings::codegen::Bindings::GamepadButtonBinding::GamepadButton use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::{Reflector, reflect_dom_object}; use crate::dom::bindings::root::DomRoot; -use crate::dom::globalscope::GlobalScope; +use crate::dom::window::Window; use crate::script_runtime::CanGc; #[dom_struct] @@ -32,14 +32,14 @@ impl GamepadButton { } pub(crate) fn new( - global: &GlobalScope, + window: &Window, pressed: bool, touched: bool, can_gc: CanGc, ) -> DomRoot<GamepadButton> { reflect_dom_object( Box::new(GamepadButton::new_inherited(pressed, touched)), - global, + window, can_gc, ) } diff --git a/components/script/dom/gamepadbuttonlist.rs b/components/script/dom/gamepadbuttonlist.rs index 50d9c8172bc..7e540ab56bb 100644 --- a/components/script/dom/gamepadbuttonlist.rs +++ b/components/script/dom/gamepadbuttonlist.rs @@ -8,7 +8,7 @@ use crate::dom::bindings::codegen::Bindings::GamepadButtonListBinding::GamepadBu use crate::dom::bindings::reflector::{Reflector, reflect_dom_object}; use crate::dom::bindings::root::{Dom, DomRoot, DomSlice}; use crate::dom::gamepadbutton::GamepadButton; -use crate::dom::globalscope::GlobalScope; +use crate::dom::window::Window; use crate::script_runtime::CanGc; // https://w3c.github.io/gamepad/#gamepadbutton-interface @@ -28,13 +28,13 @@ impl GamepadButtonList { } pub(crate) fn new( - global: &GlobalScope, + window: &Window, list: &[&GamepadButton], can_gc: CanGc, ) -> DomRoot<GamepadButtonList> { reflect_dom_object( Box::new(GamepadButtonList::new_inherited(list)), - global, + window, can_gc, ) } @@ -62,27 +62,27 @@ impl GamepadButtonListMethods<crate::DomTypeHolder> for GamepadButtonList { impl GamepadButtonList { /// Initialize the number of buttons in the "standard" gamepad mapping. /// <https://www.w3.org/TR/gamepad/#dfn-initializing-buttons> - pub(crate) fn init_buttons(global: &GlobalScope, can_gc: CanGc) -> DomRoot<GamepadButtonList> { + pub(crate) fn init_buttons(window: &Window, can_gc: CanGc) -> DomRoot<GamepadButtonList> { let standard_buttons = &[ - GamepadButton::new(global, false, false, can_gc), // Bottom button in right cluster - GamepadButton::new(global, false, false, can_gc), // Right button in right cluster - GamepadButton::new(global, false, false, can_gc), // Left button in right cluster - GamepadButton::new(global, false, false, can_gc), // Top button in right cluster - GamepadButton::new(global, false, false, can_gc), // Top left front button - GamepadButton::new(global, false, false, can_gc), // Top right front button - GamepadButton::new(global, false, false, can_gc), // Bottom left front button - GamepadButton::new(global, false, false, can_gc), // Bottom right front button - GamepadButton::new(global, false, false, can_gc), // Left button in center cluster - GamepadButton::new(global, false, false, can_gc), // Right button in center cluster - GamepadButton::new(global, false, false, can_gc), // Left stick pressed button - GamepadButton::new(global, false, false, can_gc), // Right stick pressed button - GamepadButton::new(global, false, false, can_gc), // Top button in left cluster - GamepadButton::new(global, false, false, can_gc), // Bottom button in left cluster - GamepadButton::new(global, false, false, can_gc), // Left button in left cluster - GamepadButton::new(global, false, false, can_gc), // Right button in left cluster - GamepadButton::new(global, false, false, can_gc), // Center button in center cluster + GamepadButton::new(window, false, false, can_gc), // Bottom button in right cluster + GamepadButton::new(window, false, false, can_gc), // Right button in right cluster + GamepadButton::new(window, false, false, can_gc), // Left button in right cluster + GamepadButton::new(window, false, false, can_gc), // Top button in right cluster + GamepadButton::new(window, false, false, can_gc), // Top left front button + GamepadButton::new(window, false, false, can_gc), // Top right front button + GamepadButton::new(window, false, false, can_gc), // Bottom left front button + GamepadButton::new(window, false, false, can_gc), // Bottom right front button + GamepadButton::new(window, false, false, can_gc), // Left button in center cluster + GamepadButton::new(window, false, false, can_gc), // Right button in center cluster + GamepadButton::new(window, false, false, can_gc), // Left stick pressed button + GamepadButton::new(window, false, false, can_gc), // Right stick pressed button + GamepadButton::new(window, false, false, can_gc), // Top button in left cluster + GamepadButton::new(window, false, false, can_gc), // Bottom button in left cluster + GamepadButton::new(window, false, false, can_gc), // Left button in left cluster + GamepadButton::new(window, false, false, can_gc), // Right button in left cluster + GamepadButton::new(window, false, false, can_gc), // Center button in center cluster ]; rooted_vec!(let buttons <- standard_buttons.iter().map(DomRoot::as_traced)); - Self::new(global, buttons.r(), can_gc) + Self::new(window, buttons.r(), can_gc) } } diff --git a/components/script/dom/gamepadhapticactuator.rs b/components/script/dom/gamepadhapticactuator.rs index d19db6d1279..ddea21b97ee 100644 --- a/components/script/dom/gamepadhapticactuator.rs +++ b/components/script/dom/gamepadhapticactuator.rs @@ -18,12 +18,12 @@ use crate::dom::bindings::codegen::Bindings::GamepadHapticActuatorBinding::{ use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods; use crate::dom::bindings::error::Error; use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; -use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto}; +use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::utils::to_frozen_array; -use crate::dom::globalscope::GlobalScope; use crate::dom::promise::Promise; +use crate::dom::window::Window; use crate::realms::InRealm; use crate::script_runtime::{CanGc, JSContext}; use crate::task_source::SendableTaskSource; @@ -98,27 +98,17 @@ impl GamepadHapticActuator { } pub(crate) fn new( - global: &GlobalScope, + window: &Window, gamepad_index: u32, supported_haptic_effects: GamepadSupportedHapticEffects, can_gc: CanGc, ) -> DomRoot<GamepadHapticActuator> { - Self::new_with_proto(global, gamepad_index, supported_haptic_effects, can_gc) - } - - fn new_with_proto( - global: &GlobalScope, - gamepad_index: u32, - supported_haptic_effects: GamepadSupportedHapticEffects, - can_gc: CanGc, - ) -> DomRoot<GamepadHapticActuator> { - reflect_dom_object_with_proto( + reflect_dom_object( Box::new(GamepadHapticActuator::new_inherited( gamepad_index, supported_haptic_effects, )), - global, - None, + window, can_gc, ) } diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index efa9a9a97ab..527d03eed4e 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -18,18 +18,17 @@ use base::id::{ ServiceWorkerId, ServiceWorkerRegistrationId, WebViewId, }; use constellation_traits::{ - BlobData, BlobImpl, BroadcastMsg, FileBlob, MessagePortImpl, MessagePortMsg, PortMessageTask, - ScriptToConstellationChan, ScriptToConstellationMessage, + BlobData, BlobImpl, BroadcastMsg, FileBlob, LoadData, LoadOrigin, MessagePortImpl, + MessagePortMsg, PortMessageTask, ScriptToConstellationChan, ScriptToConstellationMessage, }; use content_security_policy::{ - CheckResult, CspList, PolicyDisposition, PolicySource, Violation, ViolationResource, + CheckResult, CspList, Destination, Initiator, NavigationCheckType, ParserMetadata, + PolicyDisposition, PolicySource, Request, Violation, ViolationResource, }; use crossbeam_channel::Sender; use devtools_traits::{PageError, ScriptToDevtoolsControlMsg}; use dom_struct::dom_struct; -use embedder_traits::{ - EmbedderMsg, GamepadEvent, GamepadSupportedHapticEffects, GamepadUpdateType, -}; +use embedder_traits::EmbedderMsg; use http::HeaderMap; use hyper_serde::Serde; use ipc_channel::ipc::{self, IpcSender}; @@ -64,6 +63,7 @@ use profile_traits::{ipc as profile_ipc, mem as profile_mem, time as profile_tim use script_bindings::interfaces::GlobalScopeHelpers; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; use timers::{TimerEventId, TimerEventRequest, TimerSource}; +use url::Origin; use uuid::Uuid; #[cfg(feature = "webgpu")] use webgpu_traits::{DeviceLostReason, WebGPUDevice}; @@ -81,9 +81,7 @@ use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function; use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{ ImageBitmapOptions, ImageBitmapSource, }; -use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorMethods; use crate::dom::bindings::codegen::Bindings::NotificationBinding::NotificationPermissionCallback; -use crate::dom::bindings::codegen::Bindings::PerformanceBinding::Performance_Binding::PerformanceMethods; use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::{ PermissionName, PermissionState, }; @@ -113,8 +111,6 @@ use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; use crate::dom::eventsource::EventSource; use crate::dom::eventtarget::EventTarget; use crate::dom::file::File; -use crate::dom::gamepad::{Gamepad, contains_user_gesture}; -use crate::dom::gamepadevent::GamepadEventType; use crate::dom::htmlscriptelement::{ScriptId, SourceCode}; use crate::dom::imagebitmap::ImageBitmap; use crate::dom::messageevent::MessageEvent; @@ -2957,6 +2953,33 @@ impl GlobalScope { is_js_evaluation_allowed == CheckResult::Allowed } + pub(crate) fn should_navigation_request_be_blocked(&self, load_data: &LoadData) -> bool { + let Some(csp_list) = self.get_csp_list() else { + return false; + }; + let request = Request { + url: load_data.url.clone().into_url(), + origin: match &load_data.load_origin { + LoadOrigin::Script(immutable_origin) => immutable_origin.clone().into_url_origin(), + _ => Origin::new_opaque(), + }, + // TODO: populate this field correctly + redirect_count: 0, + destination: Destination::None, + initiator: Initiator::None, + nonce: "".to_owned(), + integrity_metadata: "".to_owned(), + parser_metadata: ParserMetadata::None, + }; + // TODO: set correct navigation check type for form submission if applicable + let (result, violations) = + csp_list.should_navigation_request_be_blocked(&request, NavigationCheckType::Other); + + self.report_csp_violations(violations); + + result == CheckResult::Blocked + } + pub(crate) fn create_image_bitmap( &self, image: ImageBitmapSource, @@ -3287,134 +3310,6 @@ impl GlobalScope { } } - pub(crate) fn handle_gamepad_event(&self, gamepad_event: GamepadEvent) { - match gamepad_event { - GamepadEvent::Connected(index, name, bounds, supported_haptic_effects) => { - self.handle_gamepad_connect( - index.0, - name, - bounds.axis_bounds, - bounds.button_bounds, - supported_haptic_effects, - ); - }, - GamepadEvent::Disconnected(index) => { - self.handle_gamepad_disconnect(index.0); - }, - GamepadEvent::Updated(index, update_type) => { - self.receive_new_gamepad_button_or_axis(index.0, update_type); - }, - }; - } - - /// <https://www.w3.org/TR/gamepad/#dfn-gamepadconnected> - fn handle_gamepad_connect( - &self, - // As the spec actually defines how to set the gamepad index, the GilRs index - // is currently unused, though in practice it will almost always be the same. - // More infra is currently needed to track gamepads across windows. - _index: usize, - name: String, - axis_bounds: (f64, f64), - button_bounds: (f64, f64), - supported_haptic_effects: GamepadSupportedHapticEffects, - ) { - // TODO: 2. If document is not null and is not allowed to use the "gamepad" permission, - // then abort these steps. - let this = Trusted::new(self); - self.task_manager() - .gamepad_task_source() - .queue(task!(gamepad_connected: move || { - let global = this.root(); - - if let Some(window) = global.downcast::<Window>() { - let navigator = window.Navigator(); - let selected_index = navigator.select_gamepad_index(); - let gamepad = Gamepad::new( - &global, - selected_index, - name, - "standard".into(), - axis_bounds, - button_bounds, - supported_haptic_effects, - false, - CanGc::note(), - ); - navigator.set_gamepad(selected_index as usize, &gamepad, CanGc::note()); - } - })); - } - - /// <https://www.w3.org/TR/gamepad/#dfn-gamepaddisconnected> - pub(crate) fn handle_gamepad_disconnect(&self, index: usize) { - let this = Trusted::new(self); - self.task_manager() - .gamepad_task_source() - .queue(task!(gamepad_disconnected: move || { - let global = this.root(); - if let Some(window) = global.downcast::<Window>() { - let navigator = window.Navigator(); - if let Some(gamepad) = navigator.get_gamepad(index) { - if window.Document().is_fully_active() { - gamepad.update_connected(false, gamepad.exposed(), CanGc::note()); - navigator.remove_gamepad(index); - } - } - } - })); - } - - /// <https://www.w3.org/TR/gamepad/#receiving-inputs> - pub(crate) fn receive_new_gamepad_button_or_axis( - &self, - index: usize, - update_type: GamepadUpdateType, - ) { - let this = Trusted::new(self); - - // <https://w3c.github.io/gamepad/#dfn-update-gamepad-state> - self.task_manager().gamepad_task_source().queue( - task!(update_gamepad_state: move || { - let global = this.root(); - if let Some(window) = global.downcast::<Window>() { - let navigator = window.Navigator(); - if let Some(gamepad) = navigator.get_gamepad(index) { - let current_time = global.performance().Now(); - gamepad.update_timestamp(*current_time); - match update_type { - GamepadUpdateType::Axis(index, value) => { - gamepad.map_and_normalize_axes(index, value); - }, - GamepadUpdateType::Button(index, value) => { - gamepad.map_and_normalize_buttons(index, value); - } - }; - if !navigator.has_gamepad_gesture() && contains_user_gesture(update_type) { - navigator.set_has_gamepad_gesture(true); - navigator.GetGamepads() - .iter() - .filter_map(|g| g.as_ref()) - .for_each(|gamepad| { - gamepad.set_exposed(true); - gamepad.update_timestamp(*current_time); - let new_gamepad = Trusted::new(&**gamepad); - if window.Document().is_fully_active() { - global.task_manager().gamepad_task_source().queue( - task!(update_gamepad_connect: move || { - let gamepad = new_gamepad.root(); - gamepad.notify_event(GamepadEventType::Connected, CanGc::note()); - }) - ); - } - }); - } - } - } - }) - ); - } - pub(crate) fn current_group_label(&self) -> Option<DOMString> { self.console_group_stack .borrow() diff --git a/components/script/dom/gpucanvascontext.rs b/components/script/dom/gpucanvascontext.rs index 5304d0f5d3b..f47e1dfddd1 100644 --- a/components/script/dom/gpucanvascontext.rs +++ b/components/script/dom/gpucanvascontext.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use script_layout_interface::HTMLCanvasDataSource; +use webrender_api::ImageKey; use crate::dom::bindings::codegen::Bindings::GPUCanvasContextBinding::GPUCanvasContextMethods; use crate::dom::bindings::codegen::UnionTypes; @@ -31,7 +31,7 @@ impl GPUCanvasContextMethods<crate::DomTypeHolder> for GPUCanvasContext { } impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, GPUCanvasContext> { - fn canvas_data_source(self) -> HTMLCanvasDataSource { + fn canvas_data_source(self) -> Option<ImageKey> { unimplemented!() } } diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index cc6df183f42..56e008839ba 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -21,7 +21,7 @@ use image::{ColorType, ImageEncoder}; use ipc_channel::ipc::{self as ipcchan}; use js::error::throw_type_error; use js::rust::{HandleObject, HandleValue}; -use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource}; +use script_layout_interface::HTMLCanvasData; use servo_media::streams::MediaStreamType; use servo_media::streams::registry::MediaStreamId; use snapshot::Snapshot; @@ -201,7 +201,7 @@ impl LayoutHTMLCanvasElementHelpers for LayoutDom<'_, HTMLCanvasElement> { Some(RenderingContext::WebGL2(context)) => context.to_layout().canvas_data_source(), #[cfg(feature = "webgpu")] Some(RenderingContext::WebGPU(context)) => context.to_layout().canvas_data_source(), - Some(RenderingContext::Placeholder(_)) | None => HTMLCanvasDataSource::Empty, + Some(RenderingContext::Placeholder(_)) | None => None, } }; diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs index e7efbde9b1d..59b71543d6d 100644 --- a/components/script/dom/htmlelement.rs +++ b/components/script/dom/htmlelement.rs @@ -116,7 +116,7 @@ impl HTMLElement { /// `.outerText` in JavaScript.` /// /// <https://html.spec.whatwg.org/multipage/#get-the-text-steps> - fn get_inner_outer_text(&self, can_gc: CanGc) -> DOMString { + pub(crate) fn get_inner_outer_text(&self, can_gc: CanGc) -> DOMString { let node = self.upcast::<Node>(); let window = node.owner_window(); let element = self.as_element(); @@ -134,6 +134,16 @@ impl HTMLElement { DOMString::from(text) } + + /// <https://html.spec.whatwg.org/multipage/#set-the-inner-text-steps> + pub(crate) fn set_inner_text(&self, input: DOMString, can_gc: CanGc) { + // Step 1: Let fragment be the rendered text fragment for value given element's node + // document. + let fragment = self.rendered_text_fragment(input, can_gc); + + // Step 2: Replace all with fragment within element. + Node::replace_all(Some(fragment.upcast()), self.upcast::<Node>(), can_gc); + } } impl HTMLElementMethods<crate::DomTypeHolder> for HTMLElement { @@ -494,12 +504,7 @@ impl HTMLElementMethods<crate::DomTypeHolder> for HTMLElement { /// <https://html.spec.whatwg.org/multipage/#set-the-inner-text-steps> fn SetInnerText(&self, input: DOMString, can_gc: CanGc) { - // Step 1: Let fragment be the rendered text fragment for value given element's node - // document. - let fragment = self.rendered_text_fragment(input, can_gc); - - // Step 2: Replace all with fragment within element. - Node::replace_all(Some(fragment.upcast()), self.upcast::<Node>(), can_gc); + self.set_inner_text(input, can_gc) } /// <https://html.spec.whatwg.org/multipage/#dom-outertext> diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index c5194c4527f..0fbff86e44a 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -162,8 +162,13 @@ impl HTMLIFrameElement { if load_data.url.scheme() == "javascript" { let window_proxy = self.GetContentWindow(); if let Some(window_proxy) = window_proxy { + if document + .global() + .should_navigation_request_be_blocked(&load_data) + { + return; + } // Important re security. See https://github.com/servo/servo/issues/23373 - // TODO: check according to https://w3c.github.io/webappsec-csp/#should-block-navigation-request if ScriptThread::check_load_origin(&load_data.load_origin, &document.url().origin()) { ScriptThread::eval_js_url(&window_proxy.global(), &mut load_data, can_gc); @@ -274,6 +279,7 @@ impl HTMLIFrameElement { Some(document.insecure_requests_policy()), document.has_trustworthy_ancestor_or_current_origin(), ); + load_data.policy_container = Some(window.as_global_scope().policy_container()); let element = self.upcast::<Element>(); load_data.srcdoc = String::from(element.get_string_attribute(&local_name!("srcdoc"))); self.navigate_or_reload_child_browsing_context( @@ -356,7 +362,7 @@ impl HTMLIFrameElement { None }; - let load_data = LoadData::new( + let mut load_data = LoadData::new( LoadOrigin::Script(document.origin().immutable().clone()), url, creator_pipeline_id, @@ -373,6 +379,10 @@ impl HTMLIFrameElement { let is_about_blank = pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get(); + if is_about_blank { + load_data.policy_container = Some(window.as_global_scope().policy_container()); + } + let history_handling = if is_about_blank { NavigationHistoryBehavior::Replace } else { @@ -402,7 +412,7 @@ impl HTMLIFrameElement { let document = self.owner_document(); let window = self.owner_window(); let pipeline_id = Some(window.pipeline_id()); - let load_data = LoadData::new( + let mut load_data = LoadData::new( LoadOrigin::Script(document.origin().immutable().clone()), url, pipeline_id, @@ -412,6 +422,7 @@ impl HTMLIFrameElement { Some(document.insecure_requests_policy()), document.has_trustworthy_ancestor_or_current_origin(), ); + load_data.policy_container = Some(window.as_global_scope().policy_container()); let browsing_context_id = BrowsingContextId::new(); let webview_id = window.window_proxy().webview_id(); self.pipeline_id.set(None); diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 58853f600d2..9d0ca807748 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -32,6 +32,7 @@ use net_traits::{ }; use servo_config::pref; use servo_url::{ImmutableOrigin, ServoUrl}; +use style::attr::AttrValue; use style::str::{HTML_SPACE_CHARACTERS, StaticStringVec}; use stylo_atoms::Atom; use uuid::Uuid; @@ -44,7 +45,9 @@ use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use crate::dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods; use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use crate::dom::bindings::codegen::GenericBindings::HTMLElementBinding::HTMLElement_Binding::HTMLElementMethods; -use crate::dom::bindings::codegen::UnionTypes::TrustedScriptURLOrUSVString; +use crate::dom::bindings::codegen::UnionTypes::{ + TrustedScriptOrString, TrustedScriptURLOrUSVString, +}; use crate::dom::bindings::error::Fallible; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::refcounted::Trusted; @@ -64,6 +67,8 @@ use crate::dom::globalscope::GlobalScope; use crate::dom::htmlelement::HTMLElement; use crate::dom::node::{ChildrenMutation, CloneChildrenFlag, Node, NodeTraits}; use crate::dom::performanceresourcetiming::InitiatorType; +use crate::dom::trustedscript::TrustedScript; +use crate::dom::trustedscripturl::TrustedScriptURL; use crate::dom::virtualmethods::VirtualMethods; use crate::fetch::create_a_potential_cors_request; use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; @@ -667,7 +672,7 @@ impl HTMLScriptElement { // Step 5. Let source text be el's child text content. // Step 6. If el has no src attribute, and source text is the empty string, then return. - let text = self.Text(); + let text = self.text(); if text.is_empty() && !element.has_attribute(&local_name!("src")) { return; } @@ -1272,6 +1277,15 @@ impl HTMLScriptElement { let event = Event::new(window.upcast(), type_, bubbles, cancelable, can_gc); event.fire(self.upcast(), can_gc) } + + fn text(&self) -> DOMString { + match self.Text() { + TrustedScriptOrString::String(value) => value, + TrustedScriptOrString::TrustedScript(trusted_script) => { + DOMString::from(trusted_script.to_string()) + }, + } + } } impl VirtualMethods for HTMLScriptElement { @@ -1286,7 +1300,7 @@ impl VirtualMethods for HTMLScriptElement { if *attr.local_name() == local_name!("src") { if let AttributeMutation::Set(_) = mutation { if !self.parser_inserted.get() && self.upcast::<Node>().is_connected() { - self.prepare(CanGc::note()); + self.prepare(can_gc); } } } @@ -1344,10 +1358,25 @@ impl VirtualMethods for HTMLScriptElement { impl HTMLScriptElementMethods<crate::DomTypeHolder> for HTMLScriptElement { // https://html.spec.whatwg.org/multipage/#dom-script-src - make_trusted_type_url_getter!(Src, "src"); + fn Src(&self) -> TrustedScriptURLOrUSVString { + let element = self.upcast::<Element>(); + element.get_trusted_type_url_attribute(&local_name!("src")) + } - // https://html.spec.whatwg.org/multipage/#dom-script-src - make_trusted_type_url_setter!(SetSrc, "src"); + /// <https://w3c.github.io/trusted-types/dist/spec/#the-src-idl-attribute> + fn SetSrc(&self, value: TrustedScriptURLOrUSVString, can_gc: CanGc) -> Fallible<()> { + let element = self.upcast::<Element>(); + let local_name = &local_name!("src"); + let value = TrustedScriptURL::get_trusted_script_url_compliant_string( + &element.owner_global(), + value, + "HTMLScriptElement", + local_name, + can_gc, + )?; + element.set_attribute(local_name, AttrValue::String(value), can_gc); + Ok(()) + } // https://html.spec.whatwg.org/multipage/#dom-script-type make_getter!(Type, "type"); @@ -1416,14 +1445,77 @@ impl HTMLScriptElementMethods<crate::DomTypeHolder> for HTMLScriptElement { // https://html.spec.whatwg.org/multipage/#dom-script-referrerpolicy make_setter!(SetReferrerPolicy, "referrerpolicy"); - // https://html.spec.whatwg.org/multipage/#dom-script-text - fn Text(&self) -> DOMString { - self.upcast::<Node>().child_text_content() + /// <https://w3c.github.io/trusted-types/dist/spec/#dom-htmlscriptelement-innertext> + fn InnerText(&self, can_gc: CanGc) -> TrustedScriptOrString { + // Step 1: Return the result of running get the text steps with this. + TrustedScriptOrString::String(self.upcast::<HTMLElement>().get_inner_outer_text(can_gc)) } - // https://html.spec.whatwg.org/multipage/#dom-script-text - fn SetText(&self, value: DOMString, can_gc: CanGc) { - self.upcast::<Node>().SetTextContent(Some(value), can_gc) + /// <https://w3c.github.io/trusted-types/dist/spec/#the-innerText-idl-attribute> + fn SetInnerText(&self, input: TrustedScriptOrString, can_gc: CanGc) -> Fallible<()> { + // Step 1: Let value be the result of calling Get Trusted Type compliant string with TrustedScript, + // this's relevant global object, the given value, HTMLScriptElement innerText, and script. + let value = TrustedScript::get_trusted_script_compliant_string( + &self.owner_global(), + input, + "HTMLScriptElement", + "innerText", + can_gc, + )?; + // Step 3: Run set the inner text steps with this and value. + self.upcast::<HTMLElement>() + .set_inner_text(DOMString::from(value), can_gc); + Ok(()) + } + + /// <https://html.spec.whatwg.org/multipage/#dom-script-text> + fn Text(&self) -> TrustedScriptOrString { + TrustedScriptOrString::String(self.upcast::<Node>().child_text_content()) + } + + /// <https://w3c.github.io/trusted-types/dist/spec/#the-text-idl-attribute> + fn SetText(&self, value: TrustedScriptOrString, can_gc: CanGc) -> Fallible<()> { + // Step 1: Let value be the result of calling Get Trusted Type compliant string with TrustedScript, + // this's relevant global object, the given value, HTMLScriptElement text, and script. + let value = TrustedScript::get_trusted_script_compliant_string( + &self.owner_global(), + value, + "HTMLScriptElement", + "text", + can_gc, + )?; + // Step 2: Set this's script text value to the given value. + // TODO: Implement for https://w3c.github.io/trusted-types/dist/spec/#prepare-script-text + // Step 3: String replace all with the given value within this. + Node::string_replace_all(DOMString::from(value), self.upcast::<Node>(), can_gc); + Ok(()) + } + + /// <https://w3c.github.io/trusted-types/dist/spec/#the-textContent-idl-attribute> + fn GetTextContent(&self) -> Option<TrustedScriptOrString> { + // Step 1: Return the result of running get text content with this. + Some(TrustedScriptOrString::String( + self.upcast::<Node>().GetTextContent()?, + )) + } + + /// <https://w3c.github.io/trusted-types/dist/spec/#the-textContent-idl-attribute> + fn SetTextContent(&self, value: Option<TrustedScriptOrString>, can_gc: CanGc) -> Fallible<()> { + // Step 1: Let value be the result of calling Get Trusted Type compliant string with TrustedScript, + // this's relevant global object, the given value, HTMLScriptElement textContent, and script. + let value = TrustedScript::get_trusted_script_compliant_string( + &self.owner_global(), + value.unwrap_or(TrustedScriptOrString::String(DOMString::from(""))), + "HTMLScriptElement", + "textContent", + can_gc, + )?; + // Step 2: Set this's script text value to value. + // TODO: Implement for https://w3c.github.io/trusted-types/dist/spec/#prepare-script-text + // Step 3: Run set text content with this and value. + self.upcast::<Node>() + .SetTextContent(Some(DOMString::from(value)), can_gc); + Ok(()) } } diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs index 997341984c6..cc44497d0b9 100644 --- a/components/script/dom/macros.rs +++ b/components/script/dom/macros.rs @@ -122,32 +122,6 @@ macro_rules! make_url_setter( ); #[macro_export] -macro_rules! make_trusted_type_url_getter( - ( $attr:ident, $htmlname:tt ) => ( - fn $attr(&self) -> TrustedScriptURLOrUSVString { - use $crate::dom::bindings::inheritance::Castable; - use $crate::dom::element::Element; - let element = self.upcast::<Element>(); - element.get_trusted_type_url_attribute(&html5ever::local_name!($htmlname)) - } - ); -); - -#[macro_export] -macro_rules! make_trusted_type_url_setter( - ( $attr:ident, $htmlname:tt ) => ( - fn $attr(&self, value: TrustedScriptURLOrUSVString, can_gc: CanGc) -> Fallible<()> { - use $crate::dom::bindings::inheritance::Castable; - use $crate::dom::element::Element; - use $crate::script_runtime::CanGc; - let element = self.upcast::<Element>(); - element.set_trusted_type_url_attribute(&html5ever::local_name!($htmlname), - value, can_gc) - } - ); -); - -#[macro_export] macro_rules! make_form_action_getter( ( $attr:ident, $htmlname:tt ) => ( fn $attr(&self) -> DOMString { diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 2caec47de25..e9d36a01426 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -110,7 +110,7 @@ use crate::dom::pointerevent::{PointerEvent, PointerId}; use crate::dom::processinginstruction::ProcessingInstruction; use crate::dom::range::WeakRangeVec; use crate::dom::raredata::NodeRareData; -use crate::dom::servoparser::serialize_html_fragment; +use crate::dom::servoparser::{ServoParser, serialize_html_fragment}; use crate::dom::shadowroot::{IsUserAgentWidget, LayoutShadowRootHelpers, ShadowRoot}; use crate::dom::stylesheetlist::StyleSheetListOwner; use crate::dom::svgsvgelement::{LayoutSVGSVGElementHelpers, SVGSVGElement}; @@ -316,6 +316,34 @@ impl Node { } } + /// Implements the "unsafely set HTML" algorithm as specified in: + /// <https://html.spec.whatwg.org/multipage/#concept-unsafely-set-html> + pub fn unsafely_set_html( + target: &Node, + context_element: &Element, + html: DOMString, + can_gc: CanGc, + ) { + // Step 1. Let newChildren be the result of the HTML fragment parsing algorithm. + let new_children = ServoParser::parse_html_fragment(context_element, html, true, can_gc); + + // Step 2. Let fragment be a new DocumentFragment whose node document is contextElement's node document. + + let context_document = context_element.owner_document(); + let fragment = DocumentFragment::new(&context_document, can_gc); + + // Step 3. For each node in newChildren, append node to fragment. + for child in new_children { + fragment + .upcast::<Node>() + .AppendChild(&child, can_gc) + .unwrap(); + } + + // Step 4. Replace all with fragment within target. + Node::replace_all(Some(fragment.upcast()), target, can_gc); + } + pub(crate) fn clean_up_style_and_layout_data(&self) { self.owner_doc().cancel_animations_for_node(self); self.style_data.borrow_mut().take(); diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index 0650fde676e..5878573d552 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -21,6 +21,7 @@ use html5ever::{Attribute, ExpandedName, LocalName, QualName, local_name, ns}; use hyper_serde::Serde; use markup5ever::TokenizerResult; use mime::{self, Mime}; +use net_traits::policy_container::PolicyContainer; use net_traits::request::RequestId; use net_traits::{ FetchMetadata, FetchResponseListener, Metadata, NetworkError, ResourceFetchTiming, @@ -813,6 +814,27 @@ impl ParserContext { pushed_entry_index: None, } } + + pub(crate) fn append_parent_to_csp_list(&self, policy_container: Option<&PolicyContainer>) { + let Some(policy_container) = policy_container else { + return; + }; + let Some(parent_csp_list) = &policy_container.csp_list else { + return; + }; + let Some(parser) = self.parser.as_ref().map(|p| p.root()) else { + return; + }; + let new_csp_list = match parser.document.get_csp_list() { + None => parent_csp_list.clone(), + Some(original_csp_list) => { + let mut appended_csp_list = original_csp_list.clone(); + appended_csp_list.append(parent_csp_list.clone()); + appended_csp_list.to_owned() + }, + }; + parser.document.set_csp_list(Some(new_csp_list)); + } } impl FetchResponseListener for ParserContext { diff --git a/components/script/dom/shadowroot.rs b/components/script/dom/shadowroot.rs index 72b074ed6f4..14d9c24b10e 100644 --- a/components/script/dom/shadowroot.rs +++ b/components/script/dom/shadowroot.rs @@ -453,6 +453,15 @@ impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot { self.slot_assignment_mode } + /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-sethtmlunsafe> + fn SetHTMLUnsafe(&self, html: DOMString, can_gc: CanGc) { + // Step 2. Unsafely set HTMl given this, this's shadow host, and complaintHTML + let target = self.upcast::<Node>(); + let context_element = self.Host(); + + Node::unsafely_set_html(target, &context_element, html, can_gc); + } + // https://dom.spec.whatwg.org/#dom-shadowroot-onslotchange event_handler!(onslotchange, GetOnslotchange, SetOnslotchange); } diff --git a/components/script/dom/trustedscript.rs b/components/script/dom/trustedscript.rs index 5ce51c24989..648fcc8c239 100644 --- a/components/script/dom/trustedscript.rs +++ b/components/script/dom/trustedscript.rs @@ -1,14 +1,19 @@ /* 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 std::fmt; use dom_struct::dom_struct; use crate::dom::bindings::codegen::Bindings::TrustedScriptBinding::TrustedScriptMethods; +use crate::dom::bindings::codegen::UnionTypes::TrustedScriptOrString; +use crate::dom::bindings::error::Fallible; use crate::dom::bindings::reflector::{Reflector, reflect_dom_object}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; +use crate::dom::trustedtypepolicy::TrustedType; +use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory; use crate::script_runtime::CanGc; #[dom_struct] @@ -30,6 +35,37 @@ impl TrustedScript { pub(crate) fn new(data: String, global: &GlobalScope, can_gc: CanGc) -> DomRoot<Self> { reflect_dom_object(Box::new(Self::new_inherited(data)), global, can_gc) } + + pub(crate) fn get_trusted_script_compliant_string( + global: &GlobalScope, + value: TrustedScriptOrString, + containing_class: &str, + field: &str, + can_gc: CanGc, + ) -> Fallible<String> { + match value { + TrustedScriptOrString::String(value) => { + let sink = format!("{} {}", containing_class, field); + TrustedTypePolicyFactory::get_trusted_type_compliant_string( + TrustedType::TrustedScript, + global, + value.as_ref().to_owned(), + &sink, + "'script'", + can_gc, + ) + }, + + TrustedScriptOrString::TrustedScript(trusted_script) => Ok(trusted_script.to_string()), + } + } +} + +impl fmt::Display for TrustedScript { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&self.data) + } } impl TrustedScriptMethods<crate::DomTypeHolder> for TrustedScript { diff --git a/components/script/dom/trustedscripturl.rs b/components/script/dom/trustedscripturl.rs index ba1e0335abc..3f0aef248b3 100644 --- a/components/script/dom/trustedscripturl.rs +++ b/components/script/dom/trustedscripturl.rs @@ -7,10 +7,14 @@ use std::fmt; use dom_struct::dom_struct; use crate::dom::bindings::codegen::Bindings::TrustedScriptURLBinding::TrustedScriptURLMethods; +use crate::dom::bindings::codegen::UnionTypes::TrustedScriptURLOrUSVString; +use crate::dom::bindings::error::Fallible; use crate::dom::bindings::reflector::{Reflector, reflect_dom_object}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; +use crate::dom::trustedtypepolicy::TrustedType; +use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory; use crate::script_runtime::CanGc; #[dom_struct] @@ -32,6 +36,31 @@ impl TrustedScriptURL { pub(crate) fn new(data: String, global: &GlobalScope, can_gc: CanGc) -> DomRoot<Self> { reflect_dom_object(Box::new(Self::new_inherited(data)), global, can_gc) } + + pub(crate) fn get_trusted_script_url_compliant_string( + global: &GlobalScope, + value: TrustedScriptURLOrUSVString, + containing_class: &str, + field: &str, + can_gc: CanGc, + ) -> Fallible<String> { + match value { + TrustedScriptURLOrUSVString::USVString(value) => { + let sink = format!("{} {}", containing_class, field); + TrustedTypePolicyFactory::get_trusted_type_compliant_string( + TrustedType::TrustedScriptURL, + global, + value.as_ref().to_owned(), + &sink, + "'script'", + can_gc, + ) + }, + TrustedScriptURLOrUSVString::TrustedScriptURL(trusted_script_url) => { + Ok(trusted_script_url.to_string()) + }, + } + } } impl fmt::Display for TrustedScriptURL { diff --git a/components/script/dom/trustedtypepolicy.rs b/components/script/dom/trustedtypepolicy.rs index 2ec5015eb88..d4def7269ed 100644 --- a/components/script/dom/trustedtypepolicy.rs +++ b/components/script/dom/trustedtypepolicy.rs @@ -7,6 +7,7 @@ use std::rc::Rc; use dom_struct::dom_struct; use js::jsapi::JSObject; use js::rust::HandleValue; +use strum_macros::IntoStaticStr; use crate::dom::bindings::callback::ExceptionHandling; use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyBinding::TrustedTypePolicyMethods; @@ -38,6 +39,13 @@ pub struct TrustedTypePolicy { create_script_url: Option<Rc<CreateScriptURLCallback>>, } +#[derive(Clone, IntoStaticStr)] +pub(crate) enum TrustedType { + TrustedHTML, + TrustedScript, + TrustedScriptURL, +} + impl TrustedTypePolicy { fn new_inherited(name: String, options: &TrustedTypePolicyOptions) -> Self { Self { @@ -59,51 +67,87 @@ impl TrustedTypePolicy { reflect_dom_object(Box::new(Self::new_inherited(name, options)), global, can_gc) } - // TODO(36258): Remove when we refactor get_trusted_type_policy_value to take an enum - // value to handle which callback to call. The callback should not be exposed outside - // of the policy object, but is currently used in TrustedPolicyFactory::process_value_with_default_policy - pub(crate) fn create_script_url(&self) -> Option<Rc<CreateScriptURLCallback>> { - self.create_script_url.clone() + /// <https://w3c.github.io/trusted-types/dist/spec/#get-trusted-type-policy-value-algorithm> + fn check_callback_if_missing(throw_if_missing: bool) -> Fallible<Option<String>> { + // Step 3.1: If throwIfMissing throw a TypeError. + if throw_if_missing { + Err(Type("Cannot find type".to_owned())) + } else { + // Step 3.2: Else return null. + Ok(None) + } } - /// This does not take all arguments as specified. That's because the return type of the - /// trusted type function and object are not the same. 2 of the 3 string callbacks return - /// a DOMString, while the other one returns an USVString. Additionally, all three callbacks - /// have a unique type signature in WebIDL. - /// - /// To circumvent these type problems, rather than implementing the full functionality here, - /// part of the algorithm is implemented on the caller side. There, we only call the callback - /// and create the object. The rest of the machinery is ensuring the right values pass through - /// to the relevant callbacks. - /// /// <https://w3c.github.io/trusted-types/dist/spec/#get-trusted-type-policy-value-algorithm> - pub(crate) fn get_trusted_type_policy_value<S, PolicyCallback>( + pub(crate) fn get_trusted_type_policy_value( &self, - policy_value_callback: PolicyCallback, + expected_type: TrustedType, + cx: JSContext, + input: DOMString, + arguments: Vec<HandleValue>, throw_if_missing: bool, - ) -> Fallible<Option<S>> - where - S: AsRef<str>, - PolicyCallback: FnOnce() -> Option<Fallible<Option<S>>>, - { + can_gc: CanGc, + ) -> Fallible<Option<String>> { + rooted!(in(*cx) let this_object: *mut JSObject); // Step 1: Let functionName be a function name for the given trustedTypeName, based on the following table: - // Step 2: Let function be policy’s options[functionName]. - let function = policy_value_callback(); - match function { - // Step 3: If function is null, then: - None => { - // Step 3.1: If throwIfMissing throw a TypeError. - if throw_if_missing { - Err(Type("Cannot find type".to_owned())) - } else { - // Step 3.2: Else return null. - Ok(None) - } + match expected_type { + TrustedType::TrustedHTML => match &self.create_html { + // Step 3: If function is null, then: + None => TrustedTypePolicy::check_callback_if_missing(throw_if_missing), + // Step 2: Let function be policy’s options[functionName]. + Some(callback) => { + // Step 4: Let policyValue be the result of invoking function with value as a first argument, + // items of arguments as subsequent arguments, and callback **this** value set to null, + // rethrowing any exceptions. + callback + .Call_( + &this_object.handle(), + input, + arguments, + ExceptionHandling::Rethrow, + can_gc, + ) + .map(|result| result.map(|str| str.as_ref().to_owned())) + }, + }, + TrustedType::TrustedScript => match &self.create_script { + // Step 3: If function is null, then: + None => TrustedTypePolicy::check_callback_if_missing(throw_if_missing), + // Step 2: Let function be policy’s options[functionName]. + Some(callback) => { + // Step 4: Let policyValue be the result of invoking function with value as a first argument, + // items of arguments as subsequent arguments, and callback **this** value set to null, + // rethrowing any exceptions. + callback + .Call_( + &this_object.handle(), + input, + arguments, + ExceptionHandling::Rethrow, + can_gc, + ) + .map(|result| result.map(|str| str.as_ref().to_owned())) + }, + }, + TrustedType::TrustedScriptURL => match &self.create_script_url { + // Step 3: If function is null, then: + None => TrustedTypePolicy::check_callback_if_missing(throw_if_missing), + // Step 2: Let function be policy’s options[functionName]. + Some(callback) => { + // Step 4: Let policyValue be the result of invoking function with value as a first argument, + // items of arguments as subsequent arguments, and callback **this** value set to null, + // rethrowing any exceptions. + callback + .Call_( + &this_object.handle(), + input, + arguments, + ExceptionHandling::Rethrow, + can_gc, + ) + .map(|result| result.map(|str| str.as_ref().to_owned())) + }, }, - // Step 4: Let policyValue be the result of invoking function with value as a first argument, - // items of arguments as subsequent arguments, and callback **this** value set to null, - // rethrowing any exceptions. - Some(policy_value) => policy_value, } } @@ -118,27 +162,30 @@ impl TrustedTypePolicy { /// to the relevant callbacks. /// /// <https://w3c.github.io/trusted-types/dist/spec/#create-a-trusted-type-algorithm> - pub(crate) fn create_trusted_type<R, S, PolicyCallback, TrustedTypeCallback>( + pub(crate) fn create_trusted_type<R, TrustedTypeCallback>( &self, - policy_value_callback: PolicyCallback, + expected_type: TrustedType, + cx: JSContext, + input: DOMString, + arguments: Vec<HandleValue>, trusted_type_creation_callback: TrustedTypeCallback, + can_gc: CanGc, ) -> Fallible<DomRoot<R>> where R: DomObject, - S: AsRef<str>, - PolicyCallback: FnOnce() -> Option<Fallible<Option<S>>>, TrustedTypeCallback: FnOnce(String) -> DomRoot<R>, { // Step 1: Let policyValue be the result of executing Get Trusted Type policy value // with the same arguments as this algorithm and additionally true as throwIfMissing. - let policy_value = self.get_trusted_type_policy_value(policy_value_callback, true); + let policy_value = + self.get_trusted_type_policy_value(expected_type, cx, input, arguments, true, can_gc); match policy_value { // Step 2: If the algorithm threw an error, rethrow the error and abort the following steps. Err(error) => Err(error), Ok(policy_value) => { // Step 3: Let dataString be the result of stringifying policyValue. let data_string = match policy_value { - Some(value) => value.as_ref().into(), + Some(value) => value, // Step 4: If policyValue is null or undefined, set dataString to the empty string. None => "".to_owned(), }; @@ -164,22 +211,12 @@ impl TrustedTypePolicyMethods<crate::DomTypeHolder> for TrustedTypePolicy { can_gc: CanGc, ) -> Fallible<DomRoot<TrustedHTML>> { self.create_trusted_type( - || { - self.create_html.clone().map(|callback| { - rooted!(in(*cx) let this_object: *mut JSObject); - // Step 4: Let policyValue be the result of invoking function with value as a first argument, - // items of arguments as subsequent arguments, and callback **this** value set to null, - // rethrowing any exceptions. - callback.Call_( - &this_object.handle(), - input, - arguments, - ExceptionHandling::Rethrow, - can_gc, - ) - }) - }, + TrustedType::TrustedHTML, + cx, + input, + arguments, |data_string| TrustedHTML::new(data_string, &self.global(), can_gc), + can_gc, ) } /// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-createscript> @@ -191,22 +228,12 @@ impl TrustedTypePolicyMethods<crate::DomTypeHolder> for TrustedTypePolicy { can_gc: CanGc, ) -> Fallible<DomRoot<TrustedScript>> { self.create_trusted_type( - || { - self.create_script.clone().map(|callback| { - rooted!(in(*cx) let this_object: *mut JSObject); - // Step 4: Let policyValue be the result of invoking function with value as a first argument, - // items of arguments as subsequent arguments, and callback **this** value set to null, - // rethrowing any exceptions. - callback.Call_( - &this_object.handle(), - input, - arguments, - ExceptionHandling::Rethrow, - can_gc, - ) - }) - }, + TrustedType::TrustedScript, + cx, + input, + arguments, |data_string| TrustedScript::new(data_string, &self.global(), can_gc), + can_gc, ) } /// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicy-createscripturl> @@ -218,22 +245,12 @@ impl TrustedTypePolicyMethods<crate::DomTypeHolder> for TrustedTypePolicy { can_gc: CanGc, ) -> Fallible<DomRoot<TrustedScriptURL>> { self.create_trusted_type( - || { - self.create_script_url.clone().map(|callback| { - rooted!(in(*cx) let this_object: *mut JSObject); - // Step 4: Let policyValue be the result of invoking function with value as a first argument, - // items of arguments as subsequent arguments, and callback **this** value set to null, - // rethrowing any exceptions. - callback.Call_( - &this_object.handle(), - input, - arguments, - ExceptionHandling::Rethrow, - can_gc, - ) - }) - }, + TrustedType::TrustedScriptURL, + cx, + input, + arguments, |data_string| TrustedScriptURL::new(data_string, &self.global(), can_gc), + can_gc, ) } } diff --git a/components/script/dom/trustedtypepolicyfactory.rs b/components/script/dom/trustedtypepolicyfactory.rs index 0dcc78b7cd0..0927446b904 100644 --- a/components/script/dom/trustedtypepolicyfactory.rs +++ b/components/script/dom/trustedtypepolicyfactory.rs @@ -6,11 +6,9 @@ use std::cell::RefCell; use content_security_policy::CheckResult; use dom_struct::dom_struct; use html5ever::{LocalName, Namespace, QualName, local_name, ns}; -use js::jsapi::JSObject; use js::jsval::NullValue; use js::rust::HandleValue; -use crate::dom::bindings::callback::ExceptionHandling; use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyFactoryBinding::{ TrustedTypePolicyFactoryMethods, TrustedTypePolicyOptions, }; @@ -23,7 +21,7 @@ use crate::dom::globalscope::GlobalScope; use crate::dom::trustedhtml::TrustedHTML; use crate::dom::trustedscript::TrustedScript; use crate::dom::trustedscripturl::TrustedScriptURL; -use crate::dom::trustedtypepolicy::TrustedTypePolicy; +use crate::dom::trustedtypepolicy::{TrustedType, TrustedTypePolicy}; use crate::js::conversions::ToJSValConvertible; use crate::script_runtime::{CanGc, JSContext}; @@ -144,50 +142,40 @@ impl TrustedTypePolicyFactory { /// <https://w3c.github.io/trusted-types/dist/spec/#process-value-with-a-default-policy-algorithm> #[allow(unsafe_code)] pub(crate) fn process_value_with_default_policy( + expected_type: TrustedType, global: &GlobalScope, input: String, sink: &str, can_gc: CanGc, - ) -> Fallible<Option<DomRoot<TrustedScriptURL>>> { + ) -> Fallible<Option<String>> { // Step 1: Let defaultPolicy be the value of global’s trusted type policy factory's default policy. let global_policy_factory = global.trusted_types(can_gc); let default_policy = match global_policy_factory.default_policy.get() { - None => return Ok(Some(TrustedScriptURL::new(input, global, can_gc))), + None => return Ok(None), Some(default_policy) => default_policy, }; let cx = GlobalScope::get_cx(); // Step 2: Let policyValue be the result of executing Get Trusted Type policy value, // with the following arguments: - let policy_value = default_policy.get_trusted_type_policy_value( - || { - // TODO(36258): support other trusted types as well by changing get_trusted_type_policy_value to accept - // the trusted type as enum and call the appropriate callback based on that. - default_policy.create_script_url().map(|callback| { - rooted!(in(*cx) let this_object: *mut JSObject); - rooted!(in(*cx) let mut trusted_type_name_value = NullValue()); - unsafe { - "TrustedScriptURL".to_jsval(*cx, trusted_type_name_value.handle_mut()); - } + rooted!(in(*cx) let mut trusted_type_name_value = NullValue()); + unsafe { + let trusted_type_name: &'static str = expected_type.clone().into(); + trusted_type_name.to_jsval(*cx, trusted_type_name_value.handle_mut()); + } - rooted!(in(*cx) let mut sink_value = NullValue()); - unsafe { - sink.to_jsval(*cx, sink_value.handle_mut()); - } + rooted!(in(*cx) let mut sink_value = NullValue()); + unsafe { + sink.to_jsval(*cx, sink_value.handle_mut()); + } - let args = vec![trusted_type_name_value.handle(), sink_value.handle()]; - // Step 4: Let policyValue be the result of invoking function with value as a first argument, - // items of arguments as subsequent arguments, and callback **this** value set to null, - // rethrowing any exceptions. - callback.Call_( - &this_object.handle(), - DOMString::from(input.to_owned()), - args, - ExceptionHandling::Rethrow, - can_gc, - ) - }) - }, + let arguments = vec![trusted_type_name_value.handle(), sink_value.handle()]; + let policy_value = default_policy.get_trusted_type_policy_value( + expected_type, + cx, + DOMString::from(input.to_owned()), + arguments, false, + can_gc, ); let data_string = match policy_value { // Step 3: If the algorithm threw an error, rethrow the error and abort the following steps. @@ -196,14 +184,15 @@ impl TrustedTypePolicyFactory { // Step 4: If policyValue is null or undefined, return policyValue. None => return Ok(None), // Step 5: Let dataString be the result of stringifying policyValue. - Some(policy_value) => policy_value.as_ref().into(), + Some(policy_value) => policy_value, }, }; - Ok(Some(TrustedScriptURL::new(data_string, global, can_gc))) + Ok(Some(data_string)) } /// Step 1 is implemented by the caller /// <https://w3c.github.io/trusted-types/dist/spec/#get-trusted-type-compliant-string-algorithm> pub(crate) fn get_trusted_type_compliant_string( + expected_type: TrustedType, global: &GlobalScope, input: String, sink: &str, @@ -224,6 +213,7 @@ impl TrustedTypePolicyFactory { // Step 4: Let convertedInput be the result of executing Process value with a default policy // with the same arguments as this algorithm. let converted_input = TrustedTypePolicyFactory::process_value_with_default_policy( + expected_type, global, input.clone(), sink, @@ -252,7 +242,7 @@ impl TrustedTypePolicyFactory { } }, // Step 8: Return stringified convertedInput. - Some(converted_input) => Ok((*converted_input).to_string()), + Some(converted_input) => Ok(converted_input), } // Step 7: Assert: convertedInput is an instance of expectedType. // TODO(https://github.com/w3c/trusted-types/issues/566): Implement when spec is resolved diff --git a/components/script/dom/webgl2renderingcontext.rs b/components/script/dom/webgl2renderingcontext.rs index 416454d8719..5e538b53b5f 100644 --- a/components/script/dom/webgl2renderingcontext.rs +++ b/components/script/dom/webgl2renderingcontext.rs @@ -22,10 +22,10 @@ use js::jsval::{BooleanValue, DoubleValue, Int32Value, NullValue, ObjectValue, U use js::rust::{CustomAutoRooterGuard, HandleObject, MutableHandleValue}; use js::typedarray::{ArrayBufferView, CreateWith, Float32, Int32Array, Uint32, Uint32Array}; use script_bindings::interfaces::WebGL2RenderingContextHelpers; -use script_layout_interface::HTMLCanvasDataSource; use servo_config::pref; use snapshot::Snapshot; use url::Host; +use webrender_api::ImageKey; use crate::canvas_context::CanvasContext; use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::{ @@ -4702,7 +4702,7 @@ impl WebGL2RenderingContextMethods<crate::DomTypeHolder> for WebGL2RenderingCont impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, WebGL2RenderingContext> { #[allow(unsafe_code)] - fn canvas_data_source(self) -> HTMLCanvasDataSource { + fn canvas_data_source(self) -> Option<ImageKey> { let this = self.unsafe_get(); unsafe { (*this.base.to_layout().unsafe_get()).layout_handle() } } diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 9996a3cf504..98170f9655b 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -31,7 +31,6 @@ use js::typedarray::{ }; use net_traits::image_cache::ImageResponse; use pixels::{self, PixelFormat}; -use script_layout_interface::HTMLCanvasDataSource; use serde::{Deserialize, Serialize}; use servo_config::pref; use snapshot::Snapshot; @@ -875,9 +874,8 @@ impl WebGLRenderingContext { receiver.recv().unwrap() } - pub(crate) fn layout_handle(&self) -> HTMLCanvasDataSource { - let image_key = self.webrender_image; - HTMLCanvasDataSource::WebGL(image_key) + pub(crate) fn layout_handle(&self) -> Option<ImageKey> { + Some(self.webrender_image) } // https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/ @@ -4829,7 +4827,7 @@ impl WebGLRenderingContextMethods<crate::DomTypeHolder> for WebGLRenderingContex } impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, WebGLRenderingContext> { - fn canvas_data_source(self) -> HTMLCanvasDataSource { + fn canvas_data_source(self) -> Option<ImageKey> { (*self.unsafe_get()).layout_handle() } } diff --git a/components/script/dom/webgpu/gpucanvascontext.rs b/components/script/dom/webgpu/gpucanvascontext.rs index c81f96f651f..359b1b14003 100644 --- a/components/script/dom/webgpu/gpucanvascontext.rs +++ b/components/script/dom/webgpu/gpucanvascontext.rs @@ -8,7 +8,6 @@ use std::cell::RefCell; use arrayvec::ArrayVec; use dom_struct::dom_struct; use ipc_channel::ipc::{self}; -use script_layout_interface::HTMLCanvasDataSource; use snapshot::Snapshot; use webgpu_traits::{ ContextConfiguration, PRESENTATION_BUFFER_COUNT, WebGPU, WebGPUContextId, WebGPURequest, @@ -227,11 +226,11 @@ impl GPUCanvasContext { // Internal helper methods impl GPUCanvasContext { - fn layout_handle(&self) -> HTMLCanvasDataSource { + fn layout_handle(&self) -> Option<ImageKey> { if self.drawing_buffer.borrow().cleared { - HTMLCanvasDataSource::Empty + None } else { - HTMLCanvasDataSource::WebGPU(self.webrender_image) + Some(self.webrender_image) } } @@ -301,7 +300,7 @@ impl CanvasContext for GPUCanvasContext { } impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, GPUCanvasContext> { - fn canvas_data_source(self) -> HTMLCanvasDataSource { + fn canvas_data_source(self) -> Option<ImageKey> { (*self.unsafe_get()).layout_handle() } } diff --git a/components/script/dom/webxr/xrhittestsource.rs b/components/script/dom/webxr/xrhittestsource.rs index 0ec9560db6e..f73f8f79655 100644 --- a/components/script/dom/webxr/xrhittestsource.rs +++ b/components/script/dom/webxr/xrhittestsource.rs @@ -8,7 +8,7 @@ use webxr_api::HitTestId; use crate::dom::bindings::codegen::Bindings::XRHitTestSourceBinding::XRHitTestSourceMethods; use crate::dom::bindings::reflector::{Reflector, reflect_dom_object}; use crate::dom::bindings::root::{Dom, DomRoot}; -use crate::dom::globalscope::GlobalScope; +use crate::dom::window::Window; use crate::dom::xrsession::XRSession; use crate::script_runtime::CanGc; @@ -31,14 +31,14 @@ impl XRHitTestSource { } pub(crate) fn new( - global: &GlobalScope, + window: &Window, id: HitTestId, session: &XRSession, can_gc: CanGc, ) -> DomRoot<XRHitTestSource> { reflect_dom_object( Box::new(XRHitTestSource::new_inherited(id, session)), - global, + window, can_gc, ) } diff --git a/components/script/dom/webxr/xrinputsource.rs b/components/script/dom/webxr/xrinputsource.rs index 009b210646a..e454e785424 100644 --- a/components/script/dom/webxr/xrinputsource.rs +++ b/components/script/dom/webxr/xrinputsource.rs @@ -17,6 +17,7 @@ use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::gamepad::Gamepad; use crate::dom::globalscope::GlobalScope; +use crate::dom::window::Window; use crate::dom::xrhand::XRHand; use crate::dom::xrsession::XRSession; use crate::dom::xrspace::XRSpace; @@ -40,14 +41,14 @@ pub(crate) struct XRInputSource { impl XRInputSource { pub(crate) fn new_inherited( - global: &GlobalScope, + window: &Window, session: &XRSession, info: InputSource, can_gc: CanGc, ) -> XRInputSource { // <https://www.w3.org/TR/webxr-gamepads-module-1/#gamepad-differences> let gamepad = Gamepad::new( - global, + window, 0, "".into(), "xr-standard".into(), @@ -74,18 +75,18 @@ impl XRInputSource { #[allow(unsafe_code)] pub(crate) fn new( - global: &GlobalScope, + window: &Window, session: &XRSession, info: InputSource, can_gc: CanGc, ) -> DomRoot<XRInputSource> { let source = reflect_dom_object( - Box::new(XRInputSource::new_inherited(global, session, info, can_gc)), - global, + Box::new(XRInputSource::new_inherited(window, session, info, can_gc)), + window, can_gc, ); - let _ac = enter_realm(global); + let _ac = enter_realm(window); let cx = GlobalScope::get_cx(); unsafe { rooted!(in(*cx) let mut profiles = UndefinedValue()); diff --git a/components/script/dom/webxr/xrinputsourcearray.rs b/components/script/dom/webxr/xrinputsourcearray.rs index d7dcdfcbb6d..26a2c42f598 100644 --- a/components/script/dom/webxr/xrinputsourcearray.rs +++ b/components/script/dom/webxr/xrinputsourcearray.rs @@ -11,7 +11,7 @@ use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::event::Event; -use crate::dom::globalscope::GlobalScope; +use crate::dom::window::Window; use crate::dom::xrinputsource::XRInputSource; use crate::dom::xrinputsourceschangeevent::XRInputSourcesChangeEvent; use crate::dom::xrsession::XRSession; @@ -31,10 +31,10 @@ impl XRInputSourceArray { } } - pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<XRInputSourceArray> { + pub(crate) fn new(window: &Window, can_gc: CanGc) -> DomRoot<XRInputSourceArray> { reflect_dom_object( Box::new(XRInputSourceArray::new_inherited()), - global, + window, can_gc, ) } @@ -60,7 +60,7 @@ impl XRInputSourceArray { .any(|i| i.id() == info.id), "Should never add a duplicate input id!" ); - let input = XRInputSource::new(&global, session, info.clone(), can_gc); + let input = XRInputSource::new(window, session, info.clone(), can_gc); self.input_sources.borrow_mut().push(Dom::from_ref(&input)); added.push(input); } @@ -121,7 +121,7 @@ impl XRInputSourceArray { &[] }; self.input_sources.borrow_mut().retain(|i| i.id() != id); - let input = XRInputSource::new(&global, session, info, can_gc); + let input = XRInputSource::new(window, session, info, can_gc); self.input_sources.borrow_mut().push(Dom::from_ref(&input)); let added = [input]; diff --git a/components/script/dom/webxr/xrrenderstate.rs b/components/script/dom/webxr/xrrenderstate.rs index 3f546c2353d..d114020e16e 100644 --- a/components/script/dom/webxr/xrrenderstate.rs +++ b/components/script/dom/webxr/xrrenderstate.rs @@ -14,7 +14,7 @@ use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::bindings::utils::to_frozen_array; -use crate::dom::globalscope::GlobalScope; +use crate::dom::window::Window; use crate::dom::xrlayer::XRLayer; use crate::dom::xrwebgllayer::XRWebGLLayer; use crate::script_runtime::{CanGc, JSContext}; @@ -49,7 +49,7 @@ impl XRRenderState { } pub(crate) fn new( - global: &GlobalScope, + window: &Window, depth_near: f64, depth_far: f64, inline_vertical_fov: Option<f64>, @@ -65,14 +65,14 @@ impl XRRenderState { layer, layers, )), - global, + window, can_gc, ) } pub(crate) fn clone_object(&self) -> DomRoot<Self> { XRRenderState::new( - &self.global(), + self.global().as_window(), self.depth_near.get(), self.depth_far.get(), self.inline_vertical_fov.get(), diff --git a/components/script/dom/webxr/xrsession.rs b/components/script/dom/webxr/xrsession.rs index a171a769b71..6ead8f65445 100644 --- a/components/script/dom/webxr/xrsession.rs +++ b/components/script/dom/webxr/xrsession.rs @@ -54,8 +54,8 @@ use crate::dom::bindings::root::{Dom, DomRoot, MutDom, MutNullableDom}; use crate::dom::bindings::utils::to_frozen_array; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; -use crate::dom::globalscope::GlobalScope; use crate::dom::promise::Promise; +use crate::dom::window::Window; use crate::dom::xrboundedreferencespace::XRBoundedReferenceSpace; use crate::dom::xrframe::XRFrame; use crate::dom::xrhittestsource::XRHitTestSource; @@ -152,7 +152,7 @@ impl XRSession { } pub(crate) fn new( - global: &GlobalScope, + window: &Window, session: Session, mode: XRSessionMode, frame_receiver: IpcReceiver<Frame>, @@ -163,8 +163,8 @@ impl XRSession { } else { None }; - let render_state = XRRenderState::new(global, 0.1, 1000.0, ivfov, None, Vec::new(), can_gc); - let input_sources = XRInputSourceArray::new(global, can_gc); + let render_state = XRRenderState::new(window, 0.1, 1000.0, ivfov, None, Vec::new(), can_gc); + let input_sources = XRInputSourceArray::new(window, can_gc); let ret = reflect_dom_object( Box::new(XRSession::new_inherited( session, @@ -172,7 +172,7 @@ impl XRSession { &input_sources, mode, )), - global, + window, can_gc, ); ret.attach_event_handler(); @@ -587,7 +587,7 @@ impl XRSession { FrameUpdateEvent::HitTestSourceAdded(id) => { if let Some(promise) = self.pending_hit_test_promises.borrow_mut().remove(&id) { promise.resolve_native( - &XRHitTestSource::new(&self.global(), id, self, can_gc), + &XRHitTestSource::new(self.global().as_window(), id, self, can_gc), can_gc, ); } else { diff --git a/components/script/dom/webxr/xrsystem.rs b/components/script/dom/webxr/xrsystem.rs index eabe7a72119..9963d92fa59 100644 --- a/components/script/dom/webxr/xrsystem.rs +++ b/components/script/dom/webxr/xrsystem.rs @@ -297,7 +297,13 @@ impl XRSystem { return; }, }; - let session = XRSession::new(&self.global(), session, mode, frame_receiver, CanGc::note()); + let session = XRSession::new( + self.global().as_window(), + session, + mode, + frame_receiver, + CanGc::note(), + ); if mode == XRSessionMode::Inline { self.active_inline_sessions .borrow_mut() diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index a685bbb25f2..b115add8611 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -32,8 +32,9 @@ use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarker use dom_struct::dom_struct; use embedder_traits::user_content_manager::{UserContentManager, UserScript}; use embedder_traits::{ - AlertResponse, ConfirmResponse, EmbedderMsg, PromptResponse, SimpleDialog, Theme, - ViewportDetails, WebDriverJSError, WebDriverJSResult, + AlertResponse, ConfirmResponse, EmbedderMsg, GamepadEvent, GamepadSupportedHapticEffects, + GamepadUpdateType, PromptResponse, SimpleDialog, Theme, ViewportDetails, WebDriverJSError, + WebDriverJSResult, }; use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect}; use euclid::{Point2D, Rect, Scale, Size2D, Vector2D}; @@ -61,6 +62,8 @@ use num_traits::ToPrimitive; use profile_traits::ipc as ProfiledIpc; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; +use script_bindings::codegen::GenericBindings::NavigatorBinding::NavigatorMethods; +use script_bindings::codegen::GenericBindings::PerformanceBinding::PerformanceMethods; use script_bindings::interfaces::WindowHelpers; use script_layout_interface::{ FragmentType, Layout, PendingImageState, QueryMsg, Reflow, ReflowGoal, ReflowRequest, @@ -125,6 +128,8 @@ use crate::dom::document::{AnimationFrameCallback, Document, ReflowTriggerCondit use crate::dom::element::Element; use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; use crate::dom::eventtarget::EventTarget; +use crate::dom::gamepad::{Gamepad, contains_user_gesture}; +use crate::dom::gamepadevent::GamepadEventType; use crate::dom::globalscope::GlobalScope; use crate::dom::hashchangeevent::HashChangeEvent; use crate::dom::history::History; @@ -642,6 +647,126 @@ impl Window { pub(crate) fn font_context(&self) -> &Arc<FontContext> { &self.font_context } + + pub(crate) fn handle_gamepad_event(&self, gamepad_event: GamepadEvent) { + match gamepad_event { + GamepadEvent::Connected(index, name, bounds, supported_haptic_effects) => { + self.handle_gamepad_connect( + index.0, + name, + bounds.axis_bounds, + bounds.button_bounds, + supported_haptic_effects, + ); + }, + GamepadEvent::Disconnected(index) => { + self.handle_gamepad_disconnect(index.0); + }, + GamepadEvent::Updated(index, update_type) => { + self.receive_new_gamepad_button_or_axis(index.0, update_type); + }, + }; + } + + /// <https://www.w3.org/TR/gamepad/#dfn-gamepadconnected> + fn handle_gamepad_connect( + &self, + // As the spec actually defines how to set the gamepad index, the GilRs index + // is currently unused, though in practice it will almost always be the same. + // More infra is currently needed to track gamepads across windows. + _index: usize, + name: String, + axis_bounds: (f64, f64), + button_bounds: (f64, f64), + supported_haptic_effects: GamepadSupportedHapticEffects, + ) { + // TODO: 2. If document is not null and is not allowed to use the "gamepad" permission, + // then abort these steps. + let this = Trusted::new(self); + self.upcast::<GlobalScope>() + .task_manager() + .gamepad_task_source() + .queue(task!(gamepad_connected: move || { + let window = this.root(); + + let navigator = window.Navigator(); + let selected_index = navigator.select_gamepad_index(); + let gamepad = Gamepad::new( + &window, + selected_index, + name, + "standard".into(), + axis_bounds, + button_bounds, + supported_haptic_effects, + false, + CanGc::note(), + ); + navigator.set_gamepad(selected_index as usize, &gamepad, CanGc::note()); + })); + } + + /// <https://www.w3.org/TR/gamepad/#dfn-gamepaddisconnected> + fn handle_gamepad_disconnect(&self, index: usize) { + let this = Trusted::new(self); + self.upcast::<GlobalScope>() + .task_manager() + .gamepad_task_source() + .queue(task!(gamepad_disconnected: move || { + let window = this.root(); + let navigator = window.Navigator(); + if let Some(gamepad) = navigator.get_gamepad(index) { + if window.Document().is_fully_active() { + gamepad.update_connected(false, gamepad.exposed(), CanGc::note()); + navigator.remove_gamepad(index); + } + } + })); + } + + /// <https://www.w3.org/TR/gamepad/#receiving-inputs> + fn receive_new_gamepad_button_or_axis(&self, index: usize, update_type: GamepadUpdateType) { + let this = Trusted::new(self); + + // <https://w3c.github.io/gamepad/#dfn-update-gamepad-state> + self.upcast::<GlobalScope>().task_manager().gamepad_task_source().queue( + task!(update_gamepad_state: move || { + let window = this.root(); + let navigator = window.Navigator(); + if let Some(gamepad) = navigator.get_gamepad(index) { + let current_time = window.Performance().Now(); + gamepad.update_timestamp(*current_time); + match update_type { + GamepadUpdateType::Axis(index, value) => { + gamepad.map_and_normalize_axes(index, value); + }, + GamepadUpdateType::Button(index, value) => { + gamepad.map_and_normalize_buttons(index, value); + } + }; + if !navigator.has_gamepad_gesture() && contains_user_gesture(update_type) { + navigator.set_has_gamepad_gesture(true); + navigator.GetGamepads() + .iter() + .filter_map(|g| g.as_ref()) + .for_each(|gamepad| { + gamepad.set_exposed(true); + gamepad.update_timestamp(*current_time); + let new_gamepad = Trusted::new(&**gamepad); + if window.Document().is_fully_active() { + window.upcast::<GlobalScope>().task_manager().gamepad_task_source().queue( + task!(update_gamepad_connect: move || { + let gamepad = new_gamepad.root(); + gamepad.notify_event(GamepadEventType::Connected, CanGc::note()); + }) + ); + } + }); + } + } + }) + ); + } } // https://html.spec.whatwg.org/multipage/#atob @@ -1246,7 +1371,7 @@ impl WindowMethods<crate::DomTypeHolder> for Window { let rv = jsval_to_webdriver(cx, &self.globalscope, val, realm, can_gc); let opt_chan = self.webdriver_script_chan.borrow_mut().take(); if let Some(chan) = opt_chan { - chan.send(rv).unwrap(); + let _ = chan.send(rv); } } @@ -1255,9 +1380,9 @@ impl WindowMethods<crate::DomTypeHolder> for Window { let opt_chan = self.webdriver_script_chan.borrow_mut().take(); if let Some(chan) = opt_chan { if let Ok(rv) = rv { - chan.send(Err(WebDriverJSError::JSException(rv))).unwrap(); + let _ = chan.send(Err(WebDriverJSError::JSException(rv))); } else { - chan.send(rv).unwrap(); + let _ = chan.send(rv); } } } @@ -1265,7 +1390,7 @@ impl WindowMethods<crate::DomTypeHolder> for Window { fn WebdriverTimeout(&self) { let opt_chan = self.webdriver_script_chan.borrow_mut().take(); if let Some(chan) = opt_chan { - chan.send(Err(WebDriverJSError::Timeout)).unwrap(); + let _ = chan.send(Err(WebDriverJSError::Timeout)); } } |