diff options
Diffstat (limited to 'components/script')
24 files changed, 826 insertions, 96 deletions
diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs index 90630fb07c3..4b47308fa46 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}; @@ -180,6 +181,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 +314,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)); @@ -1019,7 +1033,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 +1052,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 +1071,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 +1092,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 +1120,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/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 77c3c776c3e..e660471364e 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,6 +2411,7 @@ 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', 'crate::dom::bindings::record::Record', 'crate::dom::bindings::num::Finite', @@ -2419,6 +2422,7 @@ 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', 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/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/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index 9e638539995..8924f8ddd1c 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}; @@ -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 e58e65ea5bb..b70663a6949 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -130,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(); @@ -150,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()), @@ -229,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); @@ -480,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 @@ -580,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/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/messageevent.rs b/components/script/dom/messageevent.rs index cb8ebaaebd1..3e9a7079605 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; + +#[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/mod.rs b/components/script/dom/mod.rs index 302ae5baad4..16d2c6fb686 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -532,6 +532,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/offscreencanvas.rs b/components/script/dom/offscreencanvas.rs index 142379519fb..69c1114768c 100644 --- a/components/script/dom/offscreencanvas.rs +++ b/components/script/dom/offscreencanvas.rs @@ -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/webgl2renderingcontext.rs b/components/script/dom/webgl2renderingcontext.rs index beff6ec6bfd..d9a3e81b46a 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; @@ -32,8 +32,10 @@ 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::{ @@ -62,6 +64,7 @@ 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>, } fn typedarray_elem_size(typeid: Type) -> usize { @@ -100,6 +103,7 @@ 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), }) } @@ -210,6 +214,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 +831,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 @@ -1721,6 +1745,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/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..c843ab578d1 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -1165,6 +1165,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 +2963,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; @@ -4481,3 +4490,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/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/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/WebGL2RenderingContext.webidl b/components/script/dom/webidls/WebGL2RenderingContext.webidl index c171a9bb866..312c7b78211 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 { // }; @@ -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/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/lib.rs b/components/script/lib.rs index 48f42cd7a38..87eec7cab02 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -6,7 +6,6 @@ #![feature(core_intrinsics)] #![feature(drain_filter)] #![feature(inner_deref)] -#![feature(on_unimplemented)] #![feature(plugin)] #![deny(unsafe_code)] #![allow(non_snake_case)] @@ -66,6 +65,7 @@ mod dom; mod canvas_state; #[warn(deprecated)] mod compartments; +mod euclidext; #[warn(deprecated)] pub mod fetch; #[warn(deprecated)] diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index 10198050ace..f240bb170d6 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,6 +28,7 @@ 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; @@ -31,6 +37,10 @@ use js::glue::{CollectServoSizes, CreateJobQueue, DeleteJobQueue, JobQueueTraps, use js::glue::{RUST_js_GetErrorMessage, StreamConsumerConsumeChunk, StreamConsumerStreamEnd}; use js::glue::{StreamConsumerNoteResponseURLs, StreamConsumerStreamError}; use js::jsapi::ContextOptionsRef; +use js::jsapi::Dispatchable; +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::{HandleObject, Heap, JobQueue}; @@ -46,6 +56,7 @@ 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; @@ -424,6 +435,16 @@ 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 Dispatchable, + ) -> bool { + false + } + InitDispatchToEventLoop(cx, Some(dispatch_to_event_loop), ptr::null_mut()); + + InitConsumeStreamCallback(cx, Some(consume_stream), Some(report_stream_error)); + let microtask_queue = Rc::new(MicrotaskQueue::default()); let job_queue = CreateJobQueue( &JOB_QUEUE_TRAPS, @@ -825,8 +846,94 @@ 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) diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 191df335ec5..20ac88b7bc1 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -2430,6 +2430,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); } @@ -3177,7 +3179,8 @@ impl ScriptThread { 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()) @@ -3838,6 +3841,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, |