diff options
Diffstat (limited to 'components/script/dom')
52 files changed, 1378 insertions, 174 deletions
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index ffd4a4952d0..a1cc4843021 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -152,7 +152,9 @@ use tendril::{StrTendril, TendrilSink}; use time::{Duration, Timespec, Tm}; use uuid::Uuid; use webgpu::{ - WebGPU, WebGPUAdapter, WebGPUBindGroupLayout, WebGPUBuffer, WebGPUDevice, WebGPUPipelineLayout, + WebGPU, WebGPUAdapter, WebGPUBindGroup, WebGPUBindGroupLayout, WebGPUBuffer, + WebGPUCommandBuffer, WebGPUCommandEncoder, WebGPUComputePipeline, WebGPUDevice, + WebGPUPipelineLayout, WebGPUShaderModule, }; use webrender_api::{DocumentId, ImageKey}; use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState}; @@ -530,10 +532,16 @@ unsafe_no_jsmanaged_fields!(RefCell<Option<WebGPU>>); unsafe_no_jsmanaged_fields!(RefCell<Identities>); unsafe_no_jsmanaged_fields!(WebGPU); unsafe_no_jsmanaged_fields!(WebGPUAdapter); -unsafe_no_jsmanaged_fields!(WebGPUDevice); unsafe_no_jsmanaged_fields!(WebGPUBuffer); +unsafe_no_jsmanaged_fields!(WebGPUBindGroup); unsafe_no_jsmanaged_fields!(WebGPUBindGroupLayout); +unsafe_no_jsmanaged_fields!(WebGPUComputePipeline); unsafe_no_jsmanaged_fields!(WebGPUPipelineLayout); +unsafe_no_jsmanaged_fields!(WebGPUShaderModule); +unsafe_no_jsmanaged_fields!(WebGPUCommandBuffer); +unsafe_no_jsmanaged_fields!(WebGPUCommandEncoder); +unsafe_no_jsmanaged_fields!(WebGPUDevice); +unsafe_no_jsmanaged_fields!(webgpu::wgpu::command::RawPass); unsafe_no_jsmanaged_fields!(GPUBufferState); unsafe_no_jsmanaged_fields!(WebXRSwapChainId); unsafe_no_jsmanaged_fields!(MediaList); diff --git a/components/script/dom/blob.rs b/components/script/dom/blob.rs index f8b29b895fd..bc8799a3d8d 100644 --- a/components/script/dom/blob.rs +++ b/components/script/dom/blob.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use crate::body::{run_array_buffer_data_algorithm, FetchedData}; use crate::dom::bindings::codegen::Bindings::BlobBinding; use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods; use crate::dom::bindings::codegen::UnionTypes::ArrayBufferOrArrayBufferViewOrBlobOrString; @@ -12,12 +13,16 @@ use crate::dom::bindings::serializable::{Serializable, StorageKey}; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::structuredclone::StructuredDataHolder; use crate::dom::globalscope::GlobalScope; +use crate::dom::promise::Promise; +use crate::realms::{AlreadyInRealm, InRealm}; use dom_struct::dom_struct; +use encoding_rs::UTF_8; use msg::constellation_msg::{BlobId, BlobIndex, PipelineNamespaceId}; use net_traits::filemanager_thread::RelativePos; use script_traits::serializable::BlobImpl; use std::collections::HashMap; use std::num::NonZeroU32; +use std::rc::Rc; use uuid::Uuid; // https://w3c.github.io/FileAPI/#blob @@ -223,6 +228,59 @@ impl BlobMethods for Blob { let blob_impl = BlobImpl::new_sliced(rel_pos, self.blob_id.clone(), type_string); Blob::new(&*self.global(), blob_impl) } + + // https://w3c.github.io/FileAPI/#text-method-algo + fn Text(&self) -> Rc<Promise> { + let global = self.global(); + let in_realm_proof = AlreadyInRealm::assert(&global); + let p = Promise::new_in_current_realm(&global, InRealm::Already(&in_realm_proof)); + let id = self.get_blob_url_id(); + global.read_file_async( + id, + p.clone(), + Box::new(|promise, bytes| match bytes { + Ok(b) => { + let (text, _, _) = UTF_8.decode(&b); + let text = DOMString::from(text); + promise.resolve_native(&text); + }, + Err(e) => { + promise.reject_error(e); + }, + }), + ); + p + } + + // https://w3c.github.io/FileAPI/#arraybuffer-method-algo + fn ArrayBuffer(&self) -> Rc<Promise> { + let global = self.global(); + let in_realm_proof = AlreadyInRealm::assert(&global); + let p = Promise::new_in_current_realm(&global, InRealm::Already(&in_realm_proof)); + + let id = self.get_blob_url_id(); + + global.read_file_async( + id, + p.clone(), + Box::new(|promise, bytes| { + match bytes { + Ok(b) => { + let cx = promise.global().get_cx(); + let result = run_array_buffer_data_algorithm(cx, b); + + match result { + Ok(FetchedData::ArrayBuffer(a)) => promise.resolve_native(&a), + Err(e) => promise.reject_error(e), + _ => panic!("Unexpected result from run_array_buffer_data_algorithm"), + } + }, + Err(e) => promise.reject_error(e), + }; + }), + ); + p + } } /// Get the normalized, MIME-parsable type string diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 371b31bbdb0..4c36a22f717 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -566,7 +566,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { &self, image: CanvasImageSource, repetition: DOMString, - ) -> Fallible<DomRoot<CanvasPattern>> { + ) -> Fallible<Option<DomRoot<CanvasPattern>>> { self.canvas_state .borrow() .create_pattern(&self.global(), image, repetition) diff --git a/components/script/dom/create.rs b/components/script/dom/create.rs index d67550ee47f..f68a680483d 100644 --- a/components/script/dom/create.rs +++ b/components/script/dom/create.rs @@ -200,6 +200,8 @@ fn create_html_element( None => { if is_valid_custom_element_name(&*name.local) { result.set_custom_element_state(CustomElementState::Undefined); + } else { + result.set_custom_element_state(CustomElementState::Uncustomized); } }, }; diff --git a/components/script/dom/domexception.rs b/components/script/dom/domexception.rs index e8d848d0b9f..e749541eace 100644 --- a/components/script/dom/domexception.rs +++ b/components/script/dom/domexception.rs @@ -32,6 +32,7 @@ pub enum DOMErrorName { NetworkError = DOMExceptionConstants::NETWORK_ERR, AbortError = DOMExceptionConstants::ABORT_ERR, TypeMismatchError = DOMExceptionConstants::TYPE_MISMATCH_ERR, + URLMismatchError = DOMExceptionConstants::URL_MISMATCH_ERR, QuotaExceededError = DOMExceptionConstants::QUOTA_EXCEEDED_ERR, TimeoutError = DOMExceptionConstants::TIMEOUT_ERR, InvalidNodeTypeError = DOMExceptionConstants::INVALID_NODE_TYPE_ERR, @@ -60,6 +61,7 @@ impl DOMErrorName { "NetworkError" => Some(DOMErrorName::NetworkError), "AbortError" => Some(DOMErrorName::AbortError), "TypeMismatchError" => Some(DOMErrorName::TypeMismatchError), + "URLMismatchError" => Some(DOMErrorName::URLMismatchError), "QuotaExceededError" => Some(DOMErrorName::QuotaExceededError), "TimeoutError" => Some(DOMErrorName::TimeoutError), "InvalidNodeTypeError" => Some(DOMErrorName::InvalidNodeTypeError), @@ -102,6 +104,7 @@ impl DOMException { DOMErrorName::NetworkError => "A network error occurred.", DOMErrorName::AbortError => "The operation was aborted.", DOMErrorName::TypeMismatchError => "The given type does not match any expected type.", + DOMErrorName::URLMismatchError => "The given URL does not match another URL.", DOMErrorName::QuotaExceededError => "The quota has been exceeded.", DOMErrorName::TimeoutError => "The operation timed out.", DOMErrorName::InvalidNodeTypeError => { diff --git a/components/script/dom/domtokenlist.rs b/components/script/dom/domtokenlist.rs index b870ab4f16d..7f56f58c502 100644 --- a/components/script/dom/domtokenlist.rs +++ b/components/script/dom/domtokenlist.rs @@ -52,6 +52,17 @@ impl DOMTokenList { slice => Ok(Atom::from(slice)), } } + + // https://dom.spec.whatwg.org/#concept-dtl-update + fn perform_update_steps(&self, atoms: Vec<Atom>) { + // Step 1 + if !self.element.has_attribute(&self.local_name) && atoms.len() == 0 { + return; + } + // step 2 + self.element + .set_atomic_tokenlist_attribute(&self.local_name, atoms) + } } // https://dom.spec.whatwg.org/#domtokenlist @@ -93,8 +104,7 @@ impl DOMTokenListMethods for DOMTokenList { atoms.push(token); } } - self.element - .set_atomic_tokenlist_attribute(&self.local_name, atoms); + self.perform_update_steps(atoms); Ok(()) } @@ -108,8 +118,7 @@ impl DOMTokenListMethods for DOMTokenList { .position(|atom| *atom == token) .map(|index| atoms.remove(index)); } - self.element - .set_atomic_tokenlist_attribute(&self.local_name, atoms); + self.perform_update_steps(atoms); Ok(()) } @@ -122,8 +131,7 @@ impl DOMTokenListMethods for DOMTokenList { Some(true) => Ok(true), _ => { atoms.remove(index); - self.element - .set_atomic_tokenlist_attribute(&self.local_name, atoms); + self.perform_update_steps(atoms); Ok(false) }, }, @@ -131,8 +139,7 @@ impl DOMTokenListMethods for DOMTokenList { Some(false) => Ok(false), _ => { atoms.push(token); - self.element - .set_atomic_tokenlist_attribute(&self.local_name, atoms); + self.perform_update_steps(atoms); Ok(true) }, }, @@ -166,14 +173,27 @@ impl DOMTokenListMethods for DOMTokenList { let mut atoms = self.element.get_tokenlist_attribute(&self.local_name); let mut result = false; if let Some(pos) = atoms.iter().position(|atom| *atom == token) { - if !atoms.contains(&new_token) { - atoms[pos] = new_token; + if let Some(redundant_pos) = atoms.iter().position(|atom| *atom == new_token) { + if redundant_pos > pos { + // The replacement is already in the list, later, + // so we perform the replacement and remove the + // later copy. + atoms[pos] = new_token; + atoms.remove(redundant_pos); + } else if redundant_pos < pos { + // The replacement is already in the list, earlier, + // so we remove the index where we'd be putting the + // later copy. + atoms.remove(pos); + } + // else we are replacing the token with itself, nothing to change } else { - atoms.remove(pos); + // The replacement is not in the list already + atoms[pos] = new_token; } + // Step 5. - self.element - .set_atomic_tokenlist_attribute(&self.local_name, atoms); + self.perform_update_steps(atoms); result = true; } Ok(result) diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 29bdaf2f3bf..c26e492d544 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -324,14 +324,23 @@ impl Element { } pub fn set_custom_element_state(&self, state: CustomElementState) { - self.ensure_rare_data().custom_element_state = state; + // no need to inflate rare data for uncustomized + if state != CustomElementState::Uncustomized || self.rare_data().is_some() { + self.ensure_rare_data().custom_element_state = state; + } + // https://dom.spec.whatwg.org/#concept-element-defined + let in_defined_state = match state { + CustomElementState::Uncustomized | CustomElementState::Custom => true, + _ => false, + }; + self.set_state(ElementState::IN_DEFINED_STATE, in_defined_state) } pub fn get_custom_element_state(&self) -> CustomElementState { if let Some(rare_data) = self.rare_data().as_ref() { return rare_data.custom_element_state; } - CustomElementState::Undefined + CustomElementState::Uncustomized } pub fn set_custom_element_definition(&self, definition: Rc<CustomElementDefinition>) { @@ -3039,6 +3048,7 @@ impl<'a> SelectorsElement for DomRoot<Element> { NonTSPseudoClass::Focus | NonTSPseudoClass::Fullscreen | NonTSPseudoClass::Hover | + NonTSPseudoClass::Defined | NonTSPseudoClass::Enabled | NonTSPseudoClass::Disabled | NonTSPseudoClass::Checked | diff --git a/components/script/dom/event.rs b/components/script/dom/event.rs index e8e7ad7251c..3c6bf956e9e 100644 --- a/components/script/dom/event.rs +++ b/components/script/dom/event.rs @@ -10,7 +10,6 @@ use crate::dom::bindings::codegen::Bindings::PerformanceBinding::DOMHighResTimeS use crate::dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceBinding::PerformanceMethods; use crate::dom::bindings::error::Fallible; use crate::dom::bindings::inheritance::Castable; -use crate::dom::bindings::num::Finite; use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::{DomRoot, DomSlice, MutNullableDom}; @@ -19,6 +18,7 @@ use crate::dom::document::Document; use crate::dom::eventtarget::{CompiledEventListener, EventTarget, ListenerPhase}; use crate::dom::globalscope::GlobalScope; use crate::dom::node::Node; +use crate::dom::performance::reduce_timing_resolution; use crate::dom::virtualmethods::vtable_for; use crate::dom::window::Window; use crate::task::TaskOnce; @@ -333,7 +333,7 @@ impl EventMethods for Event { // https://dom.spec.whatwg.org/#dom-event-timestamp fn TimeStamp(&self) -> DOMHighResTimeStamp { - Finite::wrap( + reduce_timing_resolution( (self.precise_time_ns - (*self.global().performance().TimeOrigin()).round() as u64) .to_ms(), ) @@ -419,7 +419,7 @@ pub enum EventPhase { /// helps us to prevent such events from being [sent to the constellation][msg] where it will be /// handled once again for page scrolling (which is definitely not what we'd want). /// -/// [msg]: https://doc.servo.org/script_traits/enum.ConstellationMsg.html#variant.KeyEvent +/// [msg]: https://doc.servo.org/compositing/enum.ConstellationMsg.html#variant.KeyEvent /// #[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)] pub enum EventDefault { diff --git a/components/script/dom/eventsource.rs b/components/script/dom/eventsource.rs index c3e3bbc339b..83866370527 100644 --- a/components/script/dom/eventsource.rs +++ b/components/script/dom/eventsource.rs @@ -32,7 +32,7 @@ use js::conversions::ToJSValConvertible; use js::jsval::UndefinedValue; use mime::{self, Mime}; use net_traits::request::{CacheMode, CorsSettings, Destination, RequestBuilder}; -use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata}; +use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata, FilteredMetadata}; use net_traits::{FetchResponseListener, FetchResponseMsg, NetworkError}; use net_traits::{ResourceFetchTiming, ResourceTimingType}; use servo_atoms::Atom; @@ -339,7 +339,12 @@ impl FetchResponseListener for EventSourceContext { Ok(fm) => { let meta = match fm { FetchMetadata::Unfiltered(m) => m, - FetchMetadata::Filtered { unsafe_, .. } => unsafe_, + FetchMetadata::Filtered { unsafe_, filtered } => match filtered { + FilteredMetadata::Opaque | FilteredMetadata::OpaqueRedirect => { + return self.fail_the_connection() + }, + _ => unsafe_, + }, }; let mime = match meta.content_type { None => return self.fail_the_connection(), @@ -352,7 +357,13 @@ impl FetchResponseListener for EventSourceContext { self.announce_the_connection(); }, Err(_) => { - self.reestablish_the_connection(); + // The spec advises failing here if reconnecting would be + // "futile", with no more specific advice; WPT tests + // consider a non-http(s) scheme to be futile. + match self.event_source.root().url.scheme() { + "http" | "https" => self.reestablish_the_connection(), + _ => self.fail_the_connection(), + } }, } } diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs index 396ffb75441..1d80781a2fb 100644 --- a/components/script/dom/eventtarget.rs +++ b/components/script/dom/eventtarget.rs @@ -33,6 +33,7 @@ use crate::dom::htmlformelement::FormControlElementHelpers; use crate::dom::node::document_from_node; use crate::dom::virtualmethods::VirtualMethods; use crate::dom::window::Window; +use crate::dom::workerglobalscope::WorkerGlobalScope; use crate::realms::enter_realm; use dom_struct::dom_struct; use fnv::FnvHasher; @@ -152,9 +153,9 @@ pub enum CompiledEventListener { impl CompiledEventListener { // https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm - pub fn call_or_handle_event<T: DomObject>( + pub fn call_or_handle_event( &self, - object: &T, + object: &EventTarget, event: &Event, exception_handle: ExceptionHandling, ) { @@ -167,27 +168,29 @@ impl CompiledEventListener { match *handler { CommonEventHandler::ErrorEventHandler(ref handler) => { if let Some(event) = event.downcast::<ErrorEvent>() { - let cx = object.global().get_cx(); - rooted!(in(*cx) let error = event.Error(cx)); - let return_value = handler.Call_( - object, - EventOrString::String(event.Message()), - Some(event.Filename()), - Some(event.Lineno()), - Some(event.Colno()), - Some(error.handle()), - exception_handle, - ); - // Step 4 - if let Ok(return_value) = return_value { - rooted!(in(*cx) let return_value = return_value); - if return_value.handle().is_boolean() && - return_value.handle().to_boolean() == true - { - event.upcast::<Event>().PreventDefault(); + if object.is::<Window>() || object.is::<WorkerGlobalScope>() { + let cx = object.global().get_cx(); + rooted!(in(*cx) let error = event.Error(cx)); + let return_value = handler.Call_( + object, + EventOrString::String(event.Message()), + Some(event.Filename()), + Some(event.Lineno()), + Some(event.Colno()), + Some(error.handle()), + exception_handle, + ); + // Step 4 + if let Ok(return_value) = return_value { + rooted!(in(*cx) let return_value = return_value); + if return_value.handle().is_boolean() && + return_value.handle().to_boolean() == true + { + event.upcast::<Event>().PreventDefault(); + } } + return; } - return; } let _ = handler.Call_( @@ -401,9 +404,15 @@ impl EventTarget { }); match idx { - Some(idx) => { - entries[idx].listener = - EventListenerType::Inline(listener.unwrap_or(InlineEventListener::Null)); + Some(idx) => match listener { + // Replace if there's something to replace with, + // but remove entirely if there isn't. + Some(listener) => { + entries[idx].listener = EventListenerType::Inline(listener); + }, + None => { + entries.remove(idx); + }, }, None => { if let Some(listener) = listener { diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 77cded77230..f96f1101bca 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -8,9 +8,9 @@ use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods; use crate::dom::bindings::conversions::{root_from_object, root_from_object_static}; -use crate::dom::bindings::error::{report_pending_exception, ErrorInfo}; +use crate::dom::bindings::error::{report_pending_exception, Error, ErrorInfo}; use crate::dom::bindings::inheritance::Castable; -use crate::dom::bindings::refcounted::Trusted; +use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::bindings::settings_stack::{entry_global, incumbent_global, AutoEntryScript}; @@ -31,10 +31,12 @@ use crate::dom::messageevent::MessageEvent; use crate::dom::messageport::MessagePort; use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope; use crate::dom::performance::Performance; +use crate::dom::promise::Promise; use crate::dom::window::Window; use crate::dom::workerglobalscope::WorkerGlobalScope; use crate::dom::workletglobalscope::WorkletGlobalScope; use crate::microtask::{Microtask, MicrotaskQueue, UserMicrotask}; +use crate::realms::enter_realm; use crate::script_module::ModuleTree; use crate::script_runtime::{CommonScriptMsg, JSContext as SafeJSContext, ScriptChan, ScriptPort}; use crate::script_thread::{MainThreadScriptChan, ScriptThread}; @@ -69,7 +71,9 @@ use js::rust::{HandleValue, MutableHandleValue}; use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL}; use msg::constellation_msg::{BlobId, MessagePortId, MessagePortRouterId, PipelineId}; use net_traits::blob_url_store::{get_blob_origin, BlobBuf}; -use net_traits::filemanager_thread::{FileManagerThreadMsg, ReadFileProgress, RelativePos}; +use net_traits::filemanager_thread::{ + FileManagerResult, FileManagerThreadMsg, ReadFileProgress, RelativePos, +}; use net_traits::image_cache::ImageCache; use net_traits::{CoreResourceMsg, CoreResourceThread, IpcSend, ResourceThreads}; use profile_traits::{ipc as profile_ipc, mem as profile_mem, time as profile_time}; @@ -94,7 +98,10 @@ use std::sync::Arc; use time::{get_time, Timespec}; use uuid::Uuid; use webgpu::wgpu::{ - id::{AdapterId, BindGroupLayoutId, BufferId, DeviceId, PipelineLayoutId}, + id::{ + AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandEncoderId, ComputePipelineId, + DeviceId, PipelineLayoutId, ShaderModuleId, + }, Backend, }; @@ -231,6 +238,23 @@ struct TimerListener { context: Trusted<GlobalScope>, } +/// A wrapper for the handling of file data received by the ipc router +struct FileListener { + /// State should progress as either of: + /// - Some(Empty) => Some(Receiving) => None + /// - Some(Empty) => None + state: Option<FileListenerState>, + task_source: FileReadingTaskSource, + task_canceller: TaskCanceller, +} + +struct FileListenerCallback(Box<dyn Fn(Rc<Promise>, Result<Vec<u8>, Error>) + Send>); + +enum FileListenerState { + Empty(FileListenerCallback, TrustedPromise), + Receiving(Vec<u8>, FileListenerCallback, TrustedPromise), +} + #[derive(JSTraceable, MallocSizeOf)] /// A holder of a weak reference for a DOM blob or file. pub enum BlobTracker { @@ -389,6 +413,64 @@ impl MessageListener { } } +impl FileListener { + fn handle(&mut self, msg: FileManagerResult<ReadFileProgress>) { + match msg { + Ok(ReadFileProgress::Meta(blob_buf)) => match self.state.take() { + Some(FileListenerState::Empty(callback, promise)) => { + self.state = Some(FileListenerState::Receiving( + blob_buf.bytes, + callback, + promise, + )); + }, + _ => panic!( + "Unexpected FileListenerState when receiving ReadFileProgress::Meta msg." + ), + }, + Ok(ReadFileProgress::Partial(mut bytes_in)) => match self.state.take() { + Some(FileListenerState::Receiving(mut bytes, callback, promise)) => { + bytes.append(&mut bytes_in); + self.state = Some(FileListenerState::Receiving(bytes, callback, promise)); + }, + _ => panic!( + "Unexpected FileListenerState when receiving ReadFileProgress::Partial msg." + ), + }, + Ok(ReadFileProgress::EOF) => match self.state.take() { + Some(FileListenerState::Receiving(bytes, callback, trusted_promise)) => { + let _ = self.task_source.queue_with_canceller( + task!(resolve_promise: move || { + let promise = trusted_promise.root(); + let _ac = enter_realm(&*promise.global()); + callback.0(promise, Ok(bytes)); + }), + &self.task_canceller, + ); + }, + _ => { + panic!("Unexpected FileListenerState when receiving ReadFileProgress::EOF msg.") + }, + }, + Err(_) => match self.state.take() { + Some(FileListenerState::Receiving(_, callback, trusted_promise)) | + Some(FileListenerState::Empty(callback, trusted_promise)) => { + let bytes = Err(Error::Network); + let _ = self.task_source.queue_with_canceller( + task!(reject_promise: move || { + let promise = trusted_promise.root(); + let _ac = enter_realm(&*promise.global()); + callback.0(promise, bytes); + }), + &self.task_canceller, + ); + }, + _ => panic!("Unexpected FileListenerState when receiving Err msg."), + }, + } + } +} + impl GlobalScope { pub fn new_inherited( pipeline_id: PipelineId, @@ -1251,18 +1333,59 @@ impl GlobalScope { } fn read_file(&self, id: Uuid) -> Result<Vec<u8>, ()> { + let recv = self.send_msg(id); + GlobalScope::read_msg(recv) + } + + pub fn read_file_async( + &self, + id: Uuid, + promise: Rc<Promise>, + callback: Box<dyn Fn(Rc<Promise>, Result<Vec<u8>, Error>) + Send>, + ) { + let recv = self.send_msg(id); + + let trusted_promise = TrustedPromise::new(promise); + let task_canceller = self.task_canceller(TaskSourceName::FileReading); + let task_source = self.file_reading_task_source(); + + let mut file_listener = FileListener { + state: Some(FileListenerState::Empty( + FileListenerCallback(callback), + trusted_promise, + )), + task_source, + task_canceller, + }; + + ROUTER.add_route( + recv.to_opaque(), + Box::new(move |msg| { + file_listener.handle( + msg.to() + .expect("Deserialization of file listener msg failed."), + ); + }), + ); + } + + fn send_msg(&self, id: Uuid) -> profile_ipc::IpcReceiver<FileManagerResult<ReadFileProgress>> { let resource_threads = self.resource_threads(); - let (chan, recv) = - profile_ipc::channel(self.time_profiler_chan().clone()).map_err(|_| ())?; + let (chan, recv) = profile_ipc::channel(self.time_profiler_chan().clone()).unwrap(); let origin = get_blob_origin(&self.get_url()); let check_url_validity = false; let msg = FileManagerThreadMsg::ReadFile(chan, id, check_url_validity, origin); let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg)); + recv + } + fn read_msg( + receiver: profile_ipc::IpcReceiver<FileManagerResult<ReadFileProgress>>, + ) -> Result<Vec<u8>, ()> { let mut bytes = vec![]; loop { - match recv.recv().unwrap() { + match receiver.recv().unwrap() { Ok(ReadFileProgress::Meta(mut blob_buf)) => { bytes.append(&mut blob_buf.bytes); }, @@ -1985,6 +2108,10 @@ impl GlobalScope { self.gpu_id_hub.borrow_mut().create_adapter_ids() } + pub fn wgpu_create_bind_group_id(&self, backend: Backend) -> BindGroupId { + self.gpu_id_hub.borrow_mut().create_bind_group_id(backend) + } + pub fn wgpu_create_bind_group_layout_id(&self, backend: Backend) -> BindGroupLayoutId { self.gpu_id_hub .borrow_mut() @@ -2004,6 +2131,24 @@ impl GlobalScope { .borrow_mut() .create_pipeline_layout_id(backend) } + + pub fn wgpu_create_shader_module_id(&self, backend: Backend) -> ShaderModuleId { + self.gpu_id_hub + .borrow_mut() + .create_shader_module_id(backend) + } + + pub fn wgpu_create_compute_pipeline_id(&self, backend: Backend) -> ComputePipelineId { + self.gpu_id_hub + .borrow_mut() + .create_compute_pipeline_id(backend) + } + + pub fn wgpu_create_command_encoder_id(&self, backend: Backend) -> CommandEncoderId { + self.gpu_id_hub + .borrow_mut() + .create_command_encoder_id(backend) + } } fn timestamp_in_ms(time: Timespec) -> u64 { diff --git a/components/script/dom/gpubindgroup.rs b/components/script/dom/gpubindgroup.rs new file mode 100644 index 00000000000..c14ca7a23b5 --- /dev/null +++ b/components/script/dom/gpubindgroup.rs @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::GPUBindGroupBinding::{ + GPUBindGroupBinding, GPUBindGroupMethods, +}; +use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::globalscope::GlobalScope; +use dom_struct::dom_struct; +use std::cell::Cell; +use webgpu::WebGPUBindGroup; + +#[dom_struct] +pub struct GPUBindGroup { + reflector_: Reflector, + label: DomRefCell<Option<DOMString>>, + bind_group: WebGPUBindGroup, + valid: Cell<bool>, +} + +impl GPUBindGroup { + fn new_inherited(bind_group: WebGPUBindGroup, valid: bool) -> GPUBindGroup { + Self { + reflector_: Reflector::new(), + label: DomRefCell::new(None), + bind_group, + valid: Cell::new(valid), + } + } + + pub fn new( + global: &GlobalScope, + bind_group: WebGPUBindGroup, + valid: bool, + ) -> DomRoot<GPUBindGroup> { + reflect_dom_object( + Box::new(GPUBindGroup::new_inherited(bind_group, valid)), + global, + GPUBindGroupBinding::Wrap, + ) + } +} + +impl GPUBindGroupMethods for GPUBindGroup { + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label + fn GetLabel(&self) -> Option<DOMString> { + self.label.borrow().clone() + } + + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label + fn SetLabel(&self, value: Option<DOMString>) { + *self.label.borrow_mut() = value; + } +} diff --git a/components/script/dom/gpubuffer.rs b/components/script/dom/gpubuffer.rs index 160ef6cf79c..8cc9bc52249 100644 --- a/components/script/dom/gpubuffer.rs +++ b/components/script/dom/gpubuffer.rs @@ -79,6 +79,20 @@ impl GPUBuffer { } } +impl GPUBuffer { + pub fn id(&self) -> WebGPUBuffer { + self.buffer + } + + pub fn size(&self) -> GPUBufferSize { + self.size + } + + pub fn usage(&self) -> u32 { + self.usage + } +} + impl Drop for GPUBuffer { fn drop(&mut self) { self.Destroy() diff --git a/components/script/dom/gpucommandbuffer.rs b/components/script/dom/gpucommandbuffer.rs new file mode 100644 index 00000000000..5cc4c926547 --- /dev/null +++ b/components/script/dom/gpucommandbuffer.rs @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::GPUCommandBufferBinding::{ + self, GPUCommandBufferMethods, +}; +use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::globalscope::GlobalScope; +use dom_struct::dom_struct; +use webgpu::{WebGPU, WebGPUCommandBuffer}; + +#[dom_struct] +pub struct GPUCommandBuffer { + reflector_: Reflector, + #[ignore_malloc_size_of = "defined in webgpu"] + channel: WebGPU, + label: DomRefCell<Option<DOMString>>, + command_buffer: WebGPUCommandBuffer, +} + +impl GPUCommandBuffer { + pub fn new_inherited(channel: WebGPU, command_buffer: WebGPUCommandBuffer) -> GPUCommandBuffer { + GPUCommandBuffer { + channel, + reflector_: Reflector::new(), + label: DomRefCell::new(None), + command_buffer, + } + } + + pub fn new( + global: &GlobalScope, + channel: WebGPU, + command_buffer: WebGPUCommandBuffer, + ) -> DomRoot<GPUCommandBuffer> { + reflect_dom_object( + Box::new(GPUCommandBuffer::new_inherited(channel, command_buffer)), + global, + GPUCommandBufferBinding::Wrap, + ) + } +} + +impl GPUCommandBufferMethods for GPUCommandBuffer { + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label + fn GetLabel(&self) -> Option<DOMString> { + self.label.borrow().clone() + } + + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label + fn SetLabel(&self, value: Option<DOMString>) { + *self.label.borrow_mut() = value; + } +} diff --git a/components/script/dom/gpucommandencoder.rs b/components/script/dom/gpucommandencoder.rs new file mode 100644 index 00000000000..5e86c0b8bfd --- /dev/null +++ b/components/script/dom/gpucommandencoder.rs @@ -0,0 +1,114 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::GPUCommandEncoderBinding::{ + self, GPUCommandBufferDescriptor, GPUCommandEncoderMethods, GPUComputePassDescriptor, +}; +use crate::dom::bindings::reflector::DomObject; +use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::globalscope::GlobalScope; +use crate::dom::gpubuffer::GPUBuffer; +use crate::dom::gpucommandbuffer::GPUCommandBuffer; +use crate::dom::gpucomputepassencoder::GPUComputePassEncoder; +use dom_struct::dom_struct; +use ipc_channel::ipc; +use webgpu::{wgpu::command::RawPass, WebGPU, WebGPUCommandEncoder, WebGPURequest}; + +#[dom_struct] +pub struct GPUCommandEncoder { + reflector_: Reflector, + #[ignore_malloc_size_of = "defined in webgpu"] + channel: WebGPU, + label: DomRefCell<Option<DOMString>>, + encoder: WebGPUCommandEncoder, +} + +impl GPUCommandEncoder { + pub fn new_inherited(channel: WebGPU, encoder: WebGPUCommandEncoder) -> GPUCommandEncoder { + GPUCommandEncoder { + channel, + reflector_: Reflector::new(), + label: DomRefCell::new(None), + encoder, + } + } + + pub fn new( + global: &GlobalScope, + channel: WebGPU, + encoder: WebGPUCommandEncoder, + ) -> DomRoot<GPUCommandEncoder> { + reflect_dom_object( + Box::new(GPUCommandEncoder::new_inherited(channel, encoder)), + global, + GPUCommandEncoderBinding::Wrap, + ) + } +} + +impl GPUCommandEncoderMethods for GPUCommandEncoder { + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label + fn GetLabel(&self) -> Option<DOMString> { + self.label.borrow().clone() + } + + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label + fn SetLabel(&self, value: Option<DOMString>) { + *self.label.borrow_mut() = value; + } + + /// https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-begincomputepass + fn BeginComputePass( + &self, + _descriptor: &GPUComputePassDescriptor, + ) -> DomRoot<GPUComputePassEncoder> { + GPUComputePassEncoder::new( + &self.global(), + self.channel.clone(), + RawPass::new_compute(self.encoder.0), + ) + } + + /// https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-copybuffertobuffer + fn CopyBufferToBuffer( + &self, + source: &GPUBuffer, + source_offset: u64, + destination: &GPUBuffer, + destination_offset: u64, + size: u64, + ) { + self.channel + .0 + .send(WebGPURequest::CopyBufferToBuffer( + self.encoder.0, + source.id().0, + source_offset, + destination.id().0, + destination_offset, + size, + )) + .expect("Failed to send CopyBufferToBuffer"); + } + + /// https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-finish + fn Finish(&self, _descriptor: &GPUCommandBufferDescriptor) -> DomRoot<GPUCommandBuffer> { + let (sender, receiver) = ipc::channel().unwrap(); + self.channel + .0 + .send(WebGPURequest::CommandEncoderFinish( + sender, + self.encoder.0, + // TODO(zakorgy): We should use `_descriptor` here after it's not empty + // and the underlying wgpu-core struct is serializable + )) + .expect("Failed to send CopyBufferToBuffer"); + + let buffer = receiver.recv().unwrap(); + GPUCommandBuffer::new(&self.global(), self.channel.clone(), buffer) + } +} diff --git a/components/script/dom/gpucomputepassencoder.rs b/components/script/dom/gpucomputepassencoder.rs new file mode 100644 index 00000000000..ecdd4407a8d --- /dev/null +++ b/components/script/dom/gpucomputepassencoder.rs @@ -0,0 +1,59 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::GPUComputePassEncoderBinding::{ + self, GPUComputePassEncoderMethods, +}; +use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::globalscope::GlobalScope; +use dom_struct::dom_struct; +use webgpu::{wgpu::command::RawPass, WebGPU}; + +#[dom_struct] +pub struct GPUComputePassEncoder { + reflector_: Reflector, + #[ignore_malloc_size_of = "defined in webgpu"] + channel: WebGPU, + label: DomRefCell<Option<DOMString>>, + #[ignore_malloc_size_of = "defined in wgpu-core"] + pass: RawPass, +} + +impl GPUComputePassEncoder { + pub fn new_inherited(channel: WebGPU, pass: RawPass) -> GPUComputePassEncoder { + GPUComputePassEncoder { + channel, + reflector_: Reflector::new(), + label: DomRefCell::new(None), + pass, + } + } + + pub fn new( + global: &GlobalScope, + channel: WebGPU, + pass: RawPass, + ) -> DomRoot<GPUComputePassEncoder> { + reflect_dom_object( + Box::new(GPUComputePassEncoder::new_inherited(channel, pass)), + global, + GPUComputePassEncoderBinding::Wrap, + ) + } +} + +impl GPUComputePassEncoderMethods for GPUComputePassEncoder { + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label + fn GetLabel(&self) -> Option<DOMString> { + self.label.borrow().clone() + } + + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label + fn SetLabel(&self, value: Option<DOMString>) { + *self.label.borrow_mut() = value; + } +} diff --git a/components/script/dom/gpucomputepipeline.rs b/components/script/dom/gpucomputepipeline.rs new file mode 100644 index 00000000000..32c0338a7e9 --- /dev/null +++ b/components/script/dom/gpucomputepipeline.rs @@ -0,0 +1,55 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::GPUComputePipelineBinding::{ + GPUComputePipelineBinding, GPUComputePipelineMethods, +}; +use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::reflector::Reflector; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::globalscope::GlobalScope; +use dom_struct::dom_struct; +use webgpu::WebGPUComputePipeline; + +#[dom_struct] +pub struct GPUComputePipeline { + reflector_: Reflector, + label: DomRefCell<Option<DOMString>>, + compute_pipeline: WebGPUComputePipeline, +} + +impl GPUComputePipeline { + fn new_inherited(compute_pipeline: WebGPUComputePipeline) -> GPUComputePipeline { + Self { + reflector_: Reflector::new(), + label: DomRefCell::new(None), + compute_pipeline, + } + } + + pub fn new( + global: &GlobalScope, + compute_pipeline: WebGPUComputePipeline, + ) -> DomRoot<GPUComputePipeline> { + reflect_dom_object( + Box::new(GPUComputePipeline::new_inherited(compute_pipeline)), + global, + GPUComputePipelineBinding::Wrap, + ) + } +} + +impl GPUComputePipelineMethods for GPUComputePipeline { + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label + fn GetLabel(&self) -> Option<DOMString> { + self.label.borrow().clone() + } + + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label + fn SetLabel(&self, value: Option<DOMString>) { + *self.label.borrow_mut() = value; + } +} diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs index de3efec8117..514d6ad39d1 100644 --- a/components/script/dom/gpudevice.rs +++ b/components/script/dom/gpudevice.rs @@ -6,21 +6,32 @@ use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::GPUAdapterBinding::GPULimits; +use crate::dom::bindings::codegen::Bindings::GPUBindGroupBinding::GPUBindGroupDescriptor; use crate::dom::bindings::codegen::Bindings::GPUBindGroupLayoutBinding::{ GPUBindGroupLayoutBindings, GPUBindGroupLayoutDescriptor, GPUBindingType, }; use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::GPUBufferDescriptor; -use crate::dom::bindings::codegen::Bindings::GPUDeviceBinding::{self, GPUDeviceMethods}; +use crate::dom::bindings::codegen::Bindings::GPUComputePipelineBinding::GPUComputePipelineDescriptor; +use crate::dom::bindings::codegen::Bindings::GPUDeviceBinding::{ + self, GPUCommandEncoderDescriptor, GPUDeviceMethods, +}; use crate::dom::bindings::codegen::Bindings::GPUPipelineLayoutBinding::GPUPipelineLayoutDescriptor; +use crate::dom::bindings::codegen::Bindings::GPUShaderModuleBinding::GPUShaderModuleDescriptor; +use crate::dom::bindings::codegen::UnionTypes::Uint32ArrayOrString::{String, Uint32Array}; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; +use crate::dom::bindings::trace::RootedTraceableBox; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::gpuadapter::GPUAdapter; +use crate::dom::gpubindgroup::GPUBindGroup; use crate::dom::gpubindgrouplayout::GPUBindGroupLayout; use crate::dom::gpubuffer::{GPUBuffer, GPUBufferState}; +use crate::dom::gpucommandencoder::GPUCommandEncoder; +use crate::dom::gpucomputepipeline::GPUComputePipeline; use crate::dom::gpupipelinelayout::GPUPipelineLayout; +use crate::dom::gpushadermodule::GPUShaderModule; use crate::script_runtime::JSContext as SafeJSContext; use dom_struct::dom_struct; use ipc_channel::ipc; @@ -29,7 +40,10 @@ use js::jsval::{JSVal, ObjectValue}; use js::typedarray::{ArrayBuffer, CreateWith}; use std::collections::{HashMap, HashSet}; use std::ptr::{self, NonNull}; -use webgpu::wgpu::binding_model::{BindGroupLayoutBinding, BindingType, ShaderStage}; +use webgpu::wgpu::binding_model::{ + BindGroupBinding, BindGroupLayoutBinding, BindingResource, BindingType, BufferBinding, + ShaderStage, +}; use webgpu::wgpu::resource::{BufferDescriptor, BufferUsage}; use webgpu::{WebGPU, WebGPUBuffer, WebGPUDevice, WebGPURequest}; @@ -463,4 +477,136 @@ impl GPUDeviceMethods for GPUDevice { let pipeline_layout = receiver.recv().unwrap(); GPUPipelineLayout::new(&self.global(), bind_group_layouts, pipeline_layout, valid) } + + /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbindgroup + fn CreateBindGroup(&self, descriptor: &GPUBindGroupDescriptor) -> DomRoot<GPUBindGroup> { + let alignment: u64 = 256; + let mut valid = descriptor.layout.bindings().len() == descriptor.bindings.len(); + + valid &= descriptor.bindings.iter().all(|bind| { + let buffer_size = bind.resource.buffer.size(); + let resource_size = bind.resource.size.unwrap_or(buffer_size); + let length = bind.resource.offset.checked_add(resource_size); + let usage = BufferUsage::from_bits(bind.resource.buffer.usage()).unwrap(); + + length.is_some() && + buffer_size >= length.unwrap() && // check buffer OOB + bind.resource.offset % alignment == 0 && // check alignment + bind.resource.offset < buffer_size && // on Vulkan offset must be less than size of buffer + descriptor.layout.bindings().iter().any(|layout_bind| { + let ty = match layout_bind.type_ { + GPUBindingType::Storage_buffer => BufferUsage::STORAGE, + // GPUBindingType::Readonly_storage_buffer => BufferUsage::STORAGE_READ, + GPUBindingType::Uniform_buffer => BufferUsage::UNIFORM, + _ => unimplemented!(), + }; + // binding must be present in layout + layout_bind.binding == bind.binding && + // binding must contain one buffer of its type + usage.contains(ty) + }) + }); + + let bindings = descriptor + .bindings + .iter() + .map(|bind| BindGroupBinding { + binding: bind.binding, + resource: BindingResource::Buffer(BufferBinding { + buffer: bind.resource.buffer.id().0, + offset: bind.resource.offset, + size: bind.resource.size.unwrap_or(bind.resource.buffer.size()), + }), + }) + .collect::<Vec<_>>(); + let (sender, receiver) = ipc::channel().unwrap(); + let id = self + .global() + .wgpu_create_bind_group_id(self.device.0.backend()); + self.channel + .0 + .send(WebGPURequest::CreateBindGroup( + sender, + self.device, + id, + descriptor.layout.id(), + bindings, + )) + .expect("Failed to create WebGPU BindGroup"); + + let bind_group = receiver.recv().unwrap(); + GPUBindGroup::new(&self.global(), bind_group, valid) + } + + /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createshadermodule + fn CreateShaderModule( + &self, + descriptor: RootedTraceableBox<GPUShaderModuleDescriptor>, + ) -> DomRoot<GPUShaderModule> { + let (sender, receiver) = ipc::channel().unwrap(); + let program: Vec<u32> = match &descriptor.code { + Uint32Array(program) => program.to_vec(), + String(program) => program.chars().map(|c| c as u32).collect::<Vec<u32>>(), + }; + let id = self + .global() + .wgpu_create_shader_module_id(self.device.0.backend()); + self.channel + .0 + .send(WebGPURequest::CreateShaderModule( + sender, + self.device, + id, + program, + )) + .expect("Failed to create WebGPU ShaderModule"); + + let shader_module = receiver.recv().unwrap(); + GPUShaderModule::new(&self.global(), shader_module) + } + + /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcomputepipeline + fn CreateComputePipeline( + &self, + descriptor: &GPUComputePipelineDescriptor, + ) -> DomRoot<GPUComputePipeline> { + let pipeline = descriptor.parent.layout.id(); + let program = descriptor.computeStage.module.id(); + let entry_point = descriptor.computeStage.entryPoint.to_string(); + let id = self + .global() + .wgpu_create_compute_pipeline_id(self.device.0.backend()); + let (sender, receiver) = ipc::channel().unwrap(); + self.channel + .0 + .send(WebGPURequest::CreateComputePipeline( + sender, + self.device, + id, + pipeline.0, + program.0, + entry_point, + )) + .expect("Failed to create WebGPU ComputePipeline"); + + let compute_pipeline = receiver.recv().unwrap(); + GPUComputePipeline::new(&self.global(), compute_pipeline) + } + /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createcommandencoder + fn CreateCommandEncoder( + &self, + _descriptor: &GPUCommandEncoderDescriptor, + ) -> DomRoot<GPUCommandEncoder> { + let (sender, receiver) = ipc::channel().unwrap(); + let id = self + .global() + .wgpu_create_command_encoder_id(self.device.0.backend()); + self.channel + .0 + .send(WebGPURequest::CreateCommandEncoder(sender, self.device, id)) + .expect("Failed to create WebGPU command encoder"); + let encoder = receiver.recv().unwrap(); + + GPUCommandEncoder::new(&self.global(), self.channel.clone(), encoder) + } } diff --git a/components/script/dom/gpupipelinelayout.rs b/components/script/dom/gpupipelinelayout.rs index a52bbedb7b5..40f5fd9324d 100644 --- a/components/script/dom/gpupipelinelayout.rs +++ b/components/script/dom/gpupipelinelayout.rs @@ -56,6 +56,12 @@ impl GPUPipelineLayout { } } +impl GPUPipelineLayout { + pub fn id(&self) -> WebGPUPipelineLayout { + self.pipeline_layout + } +} + impl GPUPipelineLayoutMethods for GPUPipelineLayout { /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label fn GetLabel(&self) -> Option<DOMString> { diff --git a/components/script/dom/gpushadermodule.rs b/components/script/dom/gpushadermodule.rs new file mode 100644 index 00000000000..241f0650245 --- /dev/null +++ b/components/script/dom/gpushadermodule.rs @@ -0,0 +1,60 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::GPUShaderModuleBinding::{ + self, GPUShaderModuleMethods, +}; +use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::globalscope::GlobalScope; +use dom_struct::dom_struct; +use webgpu::WebGPUShaderModule; + +#[dom_struct] +pub struct GPUShaderModule { + reflector_: Reflector, + label: DomRefCell<Option<DOMString>>, + shader_module: WebGPUShaderModule, +} + +impl GPUShaderModule { + fn new_inherited(shader_module: WebGPUShaderModule) -> GPUShaderModule { + Self { + reflector_: Reflector::new(), + label: DomRefCell::new(None), + shader_module, + } + } + + pub fn new( + global: &GlobalScope, + shader_module: WebGPUShaderModule, + ) -> DomRoot<GPUShaderModule> { + reflect_dom_object( + Box::new(GPUShaderModule::new_inherited(shader_module)), + global, + GPUShaderModuleBinding::Wrap, + ) + } +} + +impl GPUShaderModule { + pub fn id(&self) -> WebGPUShaderModule { + self.shader_module + } +} + +impl GPUShaderModuleMethods for GPUShaderModule { + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label + fn GetLabel(&self) -> Option<DOMString> { + self.label.borrow().clone() + } + + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label + fn SetLabel(&self, value: Option<DOMString>) { + *self.label.borrow_mut() = value; + } +} diff --git a/components/script/dom/htmlbuttonelement.rs b/components/script/dom/htmlbuttonelement.rs index b7c7da1e9bf..49541a2d4a0 100755 --- a/components/script/dom/htmlbuttonelement.rs +++ b/components/script/dom/htmlbuttonelement.rs @@ -307,7 +307,7 @@ impl Activatable for HTMLButtonElement { if let Some(owner) = self.form_owner() { owner.submit( SubmittedFrom::NotFromForm, - FormSubmitter::ButtonElement(self.clone()), + FormSubmitter::ButtonElement(self), ); } }, diff --git a/components/script/dom/htmlformcontrolscollection.rs b/components/script/dom/htmlformcontrolscollection.rs index 1cf969a2fdd..410244c52c1 100644 --- a/components/script/dom/htmlformcontrolscollection.rs +++ b/components/script/dom/htmlformcontrolscollection.rs @@ -5,10 +5,11 @@ use crate::dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods; use crate::dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding; use crate::dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding::HTMLFormControlsCollectionMethods; +use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods}; use crate::dom::bindings::codegen::UnionTypes::RadioNodeListOrElement; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; -use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; use crate::dom::element::Element; use crate::dom::htmlcollection::{CollectionFilter, HTMLCollection}; @@ -21,25 +22,30 @@ use dom_struct::dom_struct; #[dom_struct] pub struct HTMLFormControlsCollection { collection: HTMLCollection, + form: Dom<HTMLFormElement>, } impl HTMLFormControlsCollection { fn new_inherited( - root: &HTMLFormElement, + form: &HTMLFormElement, filter: Box<dyn CollectionFilter + 'static>, ) -> HTMLFormControlsCollection { + let root_of_form = form + .upcast::<Node>() + .GetRootNode(&GetRootNodeOptions::empty()); HTMLFormControlsCollection { - collection: HTMLCollection::new_inherited(root.upcast::<Node>(), filter), + collection: HTMLCollection::new_inherited(&*root_of_form, filter), + form: Dom::from_ref(form), } } pub fn new( window: &Window, - root: &HTMLFormElement, + form: &HTMLFormElement, filter: Box<dyn CollectionFilter + 'static>, ) -> DomRoot<HTMLFormControlsCollection> { reflect_dom_object( - Box::new(HTMLFormControlsCollection::new_inherited(root, filter)), + Box::new(HTMLFormControlsCollection::new_inherited(form, filter)), window, HTMLFormControlsCollectionBinding::Wrap, ) @@ -80,14 +86,11 @@ impl HTMLFormControlsCollectionMethods for HTMLFormControlsCollection { // Step 4-5 let global = self.global(); let window = global.as_window(); - // okay to unwrap: root's type was checked in the constructor - let collection_root = self.collection.root_node(); - let form = collection_root.downcast::<HTMLFormElement>().unwrap(); // There is only one way to get an HTMLCollection, // specifically HTMLFormElement::Elements(), // and the collection filter excludes image inputs. Some(RadioNodeListOrElement::RadioNodeList( - RadioNodeList::new_controls_except_image_inputs(window, form, name), + RadioNodeList::new_controls_except_image_inputs(window, &*self.form, name), )) } // Step 3 diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index ebbe1d49615..bf59dc0280b 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -48,6 +48,7 @@ use crate::dom::node::{Node, NodeFlags, ShadowIncluding}; use crate::dom::node::{UnbindContext, VecPreOrderInsertionHelper}; use crate::dom::nodelist::{NodeList, RadioListMode}; use crate::dom::radionodelist::RadioNodeList; +use crate::dom::submitevent::SubmitEvent; use crate::dom::validitystate::ValidationFlags; use crate::dom::virtualmethods::VirtualMethods; use crate::dom::window::Window; @@ -604,10 +605,29 @@ impl HTMLFormElement { } } // Step 7 + // spec calls this "submitterButton" but it doesn't have to be a button, + // just not be the form itself + let submitter_button = match submitter { + FormSubmitter::FormElement(f) => { + if f == self { + None + } else { + Some(f.upcast::<HTMLElement>()) + } + }, + FormSubmitter::InputElement(i) => Some(i.upcast::<HTMLElement>()), + FormSubmitter::ButtonElement(b) => Some(b.upcast::<HTMLElement>()), + }; if submit_method_flag == SubmittedFrom::NotFromForm { - let event = self - .upcast::<EventTarget>() - .fire_bubbling_cancelable_event(atom!("submit")); + let event = SubmitEvent::new( + &self.global(), + atom!("submit"), + true, + true, + submitter_button.map(|s| DomRoot::from_ref(s)), + ); + let event = event.upcast::<Event>(); + event.fire(self.upcast::<EventTarget>()); if event.DefaultPrevented() { return; } diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index a8ec28ac9cf..1cfecef1728 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -13,7 +13,7 @@ use crate::dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageE use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods; use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeBinding::NodeMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; -use crate::dom::bindings::error::Fallible; +use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomObject; @@ -164,6 +164,26 @@ impl HTMLImageElement { pub fn get_url(&self) -> Option<ServoUrl> { self.current_request.borrow().parsed_url.clone() } + // https://html.spec.whatwg.org/multipage/#check-the-usability-of-the-image-argument + pub fn is_usable(&self) -> Fallible<bool> { + // If image has an intrinsic width or intrinsic height (or both) equal to zero, then return bad. + match &self.current_request.borrow().image { + Some(image) => { + if image.width == 0 || image.height == 0 { + return Ok(false); + } + }, + None => return Ok(false), + } + + match self.current_request.borrow().state { + // If image's current request's state is broken, then throw an "InvalidStateError" DOMException. + State::Broken => Err(Error::InvalidState), + State::CompletelyAvailable => Ok(true), + // If image is not fully decodable, then return bad. + State::PartiallyAvailable | State::Unavailable => Ok(false), + } + } } /// The context required for asynchronously loading an external image. @@ -1293,7 +1313,7 @@ impl HTMLImageElement { .filter_map(DomRoot::downcast::<HTMLMapElement>) .find(|n| { n.upcast::<Element>() - .get_string_attribute(&LocalName::from("name")) == + .get_string_attribute(&local_name!("name")) == last }); diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index a8587b58ed0..88b2e688d65 100755 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -2470,7 +2470,7 @@ impl Activatable for HTMLInputElement { self.form_owner().map(|o| { o.submit( SubmittedFrom::NotFromForm, - FormSubmitter::InputElement(self.clone()), + FormSubmitter::InputElement(self), ) }); }, diff --git a/components/script/dom/identityhub.rs b/components/script/dom/identityhub.rs index 91ead1acf0a..702172f6d72 100644 --- a/components/script/dom/identityhub.rs +++ b/components/script/dom/identityhub.rs @@ -5,7 +5,10 @@ use smallvec::SmallVec; use webgpu::wgpu::{ hub::IdentityManager, - id::{AdapterId, BindGroupLayoutId, BufferId, DeviceId, PipelineLayoutId}, + id::{ + AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandEncoderId, ComputePipelineId, + DeviceId, PipelineLayoutId, ShaderModuleId, + }, Backend, }; @@ -14,8 +17,12 @@ pub struct IdentityHub { adapters: IdentityManager, devices: IdentityManager, buffers: IdentityManager, + bind_groups: IdentityManager, bind_group_layouts: IdentityManager, + compute_pipelines: IdentityManager, pipeline_layouts: IdentityManager, + shader_modules: IdentityManager, + command_encoders: IdentityManager, backend: Backend, } @@ -25,8 +32,12 @@ impl IdentityHub { adapters: IdentityManager::default(), devices: IdentityManager::default(), buffers: IdentityManager::default(), + bind_groups: IdentityManager::default(), bind_group_layouts: IdentityManager::default(), + compute_pipelines: IdentityManager::default(), pipeline_layouts: IdentityManager::default(), + shader_modules: IdentityManager::default(), + command_encoders: IdentityManager::default(), backend, } } @@ -43,13 +54,29 @@ impl IdentityHub { self.buffers.alloc(self.backend) } + fn create_bind_group_id(&mut self) -> BindGroupId { + self.bind_groups.alloc(self.backend) + } + fn create_bind_group_layout_id(&mut self) -> BindGroupLayoutId { self.bind_group_layouts.alloc(self.backend) } + fn create_compute_pipeline_id(&mut self) -> ComputePipelineId { + self.compute_pipelines.alloc(self.backend) + } + fn create_pipeline_layout_id(&mut self) -> PipelineLayoutId { self.pipeline_layouts.alloc(self.backend) } + + fn create_shader_module_id(&mut self) -> ShaderModuleId { + self.shader_modules.alloc(self.backend) + } + + pub fn create_command_encoder_id(&mut self) -> CommandEncoderId { + self.command_encoders.alloc(self.backend) + } } #[derive(Debug)] @@ -82,6 +109,20 @@ impl Identities { } } + fn select(&mut self, backend: Backend) -> &mut IdentityHub { + match backend { + #[cfg(any(target_os = "linux", target_os = "windows"))] + Backend::Vulkan => &mut self.vk_hub, + #[cfg(target_os = "windows")] + Backend::Dx12 => &mut self.dx12_hub, + #[cfg(target_os = "windows")] + Backend::Dx11 => &mut self.dx11_hub, + #[cfg(any(target_os = "ios", target_os = "macos"))] + Backend::Metal => &mut self.metal_hub, + _ => &mut self.dummy_hub, + } + } + fn hubs(&mut self) -> Vec<&mut IdentityHub> { vec![ #[cfg(any(target_os = "linux", target_os = "windows"))] @@ -97,17 +138,7 @@ impl Identities { } pub fn create_device_id(&mut self, backend: Backend) -> DeviceId { - match backend { - #[cfg(any(target_os = "linux", target_os = "windows"))] - Backend::Vulkan => self.vk_hub.create_device_id(), - #[cfg(target_os = "windows")] - Backend::Dx12 => self.dx12_hub.create_device_id(), - #[cfg(target_os = "windows")] - Backend::Dx11 => self.dx11_hub.create_device_id(), - #[cfg(any(target_os = "ios", target_os = "macos"))] - Backend::Metal => self.metal_hub.create_device_id(), - _ => self.dummy_hub.create_device_id(), - } + self.select(backend).create_device_id() } pub fn create_adapter_ids(&mut self) -> SmallVec<[AdapterId; 4]> { @@ -119,44 +150,30 @@ impl Identities { } pub fn create_buffer_id(&mut self, backend: Backend) -> BufferId { - match backend { - #[cfg(any(target_os = "linux", target_os = "windows"))] - Backend::Vulkan => self.vk_hub.create_buffer_id(), - #[cfg(target_os = "windows")] - Backend::Dx12 => self.dx12_hub.create_buffer_id(), - #[cfg(target_os = "windows")] - Backend::Dx11 => self.dx11_hub.create_buffer_id(), - #[cfg(any(target_os = "ios", target_os = "macos"))] - Backend::Metal => self.metal_hub.create_buffer_id(), - _ => self.dummy_hub.create_buffer_id(), - } + self.select(backend).create_buffer_id() + } + + pub fn create_bind_group_id(&mut self, backend: Backend) -> BindGroupId { + self.select(backend).create_bind_group_id() } pub fn create_bind_group_layout_id(&mut self, backend: Backend) -> BindGroupLayoutId { - match backend { - #[cfg(any(target_os = "linux", target_os = "windows"))] - Backend::Vulkan => self.vk_hub.create_bind_group_layout_id(), - #[cfg(target_os = "windows")] - Backend::Dx12 => self.dx12_hub.create_bind_group_layout_id(), - #[cfg(target_os = "windows")] - Backend::Dx11 => self.dx11_hub.create_bind_group_layout_id(), - #[cfg(any(target_os = "ios", target_os = "macos"))] - Backend::Metal => self.metal_hub.create_bind_group_layout_id(), - _ => self.dummy_hub.create_bind_group_layout_id(), - } + self.select(backend).create_bind_group_layout_id() + } + + pub fn create_compute_pipeline_id(&mut self, backend: Backend) -> ComputePipelineId { + self.select(backend).create_compute_pipeline_id() } pub fn create_pipeline_layout_id(&mut self, backend: Backend) -> PipelineLayoutId { - match backend { - #[cfg(any(target_os = "linux", target_os = "windows"))] - Backend::Vulkan => self.vk_hub.create_pipeline_layout_id(), - #[cfg(target_os = "windows")] - Backend::Dx12 => self.dx12_hub.create_pipeline_layout_id(), - #[cfg(target_os = "windows")] - Backend::Dx11 => self.dx11_hub.create_pipeline_layout_id(), - #[cfg(any(target_os = "ios", target_os = "macos"))] - Backend::Metal => self.metal_hub.create_pipeline_layout_id(), - _ => self.dummy_hub.create_pipeline_layout_id(), - } + self.select(backend).create_pipeline_layout_id() + } + + pub fn create_shader_module_id(&mut self, backend: Backend) -> ShaderModuleId { + self.select(backend).create_shader_module_id() + } + + pub fn create_command_encoder_id(&mut self, backend: Backend) -> CommandEncoderId { + self.select(backend).create_command_encoder_id() } } diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 2550eeda007..8636ede66ac 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -318,11 +318,17 @@ pub mod gamepadlist; pub mod globalscope; pub mod gpu; pub mod gpuadapter; +pub mod gpubindgroup; pub mod gpubindgrouplayout; pub mod gpubuffer; pub mod gpubufferusage; +pub mod gpucommandbuffer; +pub mod gpucommandencoder; +pub mod gpucomputepassencoder; +pub mod gpucomputepipeline; pub mod gpudevice; pub mod gpupipelinelayout; +pub mod gpushadermodule; pub mod gpushaderstage; pub mod hashchangeevent; pub mod headers; @@ -483,6 +489,7 @@ pub mod storageevent; pub mod stylepropertymapreadonly; pub mod stylesheet; pub mod stylesheetlist; +pub mod submitevent; pub mod svgelement; pub mod svggraphicselement; pub mod svgsvgelement; diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 0c5de3f67fb..b6d4389d441 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -165,12 +165,11 @@ bitflags! { #[doc = "Specifies whether this node needs style recalc on next reflow."] const HAS_DIRTY_DESCENDANTS = 1 << 1; - // TODO: find a better place to keep this (#4105) - // https://critic.hoppipolla.co.uk/showcomment?chain=8873 - // Perhaps using a Set in Document? + #[doc = "Specifies whether or not there is an authentic click in progress on \ this element."] const CLICK_IN_PROGRESS = 1 << 2; + #[doc = "Specifies whether this node is focusable and whether it is supposed \ to be reachable with using sequential focus navigation."] const SEQUENTIALLY_FOCUSABLE = 1 << 3; diff --git a/components/script/dom/offscreencanvas.rs b/components/script/dom/offscreencanvas.rs index 70e0573a2c4..130d2b7c49a 100644 --- a/components/script/dom/offscreencanvas.rs +++ b/components/script/dom/offscreencanvas.rs @@ -138,6 +138,10 @@ impl OffscreenCanvas { )); Some(context) } + + pub fn is_valid(&self) -> bool { + self.Width() != 0 && self.Height() != 0 + } } impl OffscreenCanvasMethods for OffscreenCanvas { diff --git a/components/script/dom/offscreencanvasrenderingcontext2d.rs b/components/script/dom/offscreencanvasrenderingcontext2d.rs index 277b5925f27..31dc8b7dec5 100644 --- a/components/script/dom/offscreencanvasrenderingcontext2d.rs +++ b/components/script/dom/offscreencanvasrenderingcontext2d.rs @@ -210,7 +210,7 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex &self, image: CanvasImageSource, repetition: DOMString, - ) -> Fallible<DomRoot<CanvasPattern>> { + ) -> Fallible<Option<DomRoot<CanvasPattern>>> { self.canvas_state .borrow() .create_pattern(&self.global(), image, repetition) diff --git a/components/script/dom/paintrenderingcontext2d.rs b/components/script/dom/paintrenderingcontext2d.rs index e4b0a3cf71e..b9adc23e52d 100644 --- a/components/script/dom/paintrenderingcontext2d.rs +++ b/components/script/dom/paintrenderingcontext2d.rs @@ -340,7 +340,7 @@ impl PaintRenderingContext2DMethods for PaintRenderingContext2D { &self, image: CanvasImageSource, repetition: DOMString, - ) -> Fallible<DomRoot<CanvasPattern>> { + ) -> Fallible<Option<DomRoot<CanvasPattern>>> { self.context.CreatePattern(image, repetition) } diff --git a/components/script/dom/performance.rs b/components/script/dom/performance.rs index 82eec0e4996..9de98852df7 100644 --- a/components/script/dom/performance.rs +++ b/components/script/dom/performance.rs @@ -426,12 +426,12 @@ impl PerformanceMethods for Performance { // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HighResolutionTime/Overview.html#dom-performance-now fn Now(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.now()) + reduce_timing_resolution(self.now()) } // https://www.w3.org/TR/hr-time-2/#dom-performance-timeorigin fn TimeOrigin(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.navigation_start_precise as f64) + reduce_timing_resolution(self.navigation_start_precise as f64) } // https://www.w3.org/TR/performance-timeline-2/#dom-performance-getentries @@ -550,3 +550,14 @@ impl PerformanceMethods for Performance { SetOnresourcetimingbufferfull ); } + +// https://www.w3.org/TR/hr-time-2/#clock-resolution +pub fn reduce_timing_resolution(exact: f64) -> DOMHighResTimeStamp { + // We need a granularity no finer than 5 microseconds. + // 5 microseconds isn't an exactly representable f64 so WPT tests + // might occasionally corner-case on rounding. + // web-platform-tests/wpt#21526 wants us to use an integer number of + // microseconds; the next divisor of milliseconds up from 5 microseconds + // is 10, which is 1/100th of a millisecond. + Finite::wrap((exact * 100.0).floor() / 100.0) +} diff --git a/components/script/dom/performanceentry.rs b/components/script/dom/performanceentry.rs index 334d8b6de85..e41cbc81667 100644 --- a/components/script/dom/performanceentry.rs +++ b/components/script/dom/performanceentry.rs @@ -2,13 +2,14 @@ * 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::PerformanceBinding::DOMHighResTimeStamp; use crate::dom::bindings::codegen::Bindings::PerformanceEntryBinding; use crate::dom::bindings::codegen::Bindings::PerformanceEntryBinding::PerformanceEntryMethods; -use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; +use crate::dom::performance::reduce_timing_resolution; use dom_struct::dom_struct; #[dom_struct] @@ -77,12 +78,12 @@ impl PerformanceEntryMethods for PerformanceEntry { } // https://w3c.github.io/performance-timeline/#dom-performanceentry-starttime - fn StartTime(&self) -> Finite<f64> { - Finite::wrap(self.start_time) + fn StartTime(&self) -> DOMHighResTimeStamp { + reduce_timing_resolution(self.start_time) } // https://w3c.github.io/performance-timeline/#dom-performanceentry-duration - fn Duration(&self) -> Finite<f64> { - Finite::wrap(self.duration) + fn Duration(&self) -> DOMHighResTimeStamp { + reduce_timing_resolution(self.duration) } } diff --git a/components/script/dom/performanceresourcetiming.rs b/components/script/dom/performanceresourcetiming.rs index 586515d4365..574016adc88 100644 --- a/components/script/dom/performanceresourcetiming.rs +++ b/components/script/dom/performanceresourcetiming.rs @@ -6,11 +6,11 @@ use crate::dom::bindings::codegen::Bindings::PerformanceBinding::DOMHighResTimeS use crate::dom::bindings::codegen::Bindings::PerformanceResourceTimingBinding::{ self, PerformanceResourceTimingMethods, }; -use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::reflect_dom_object; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; +use crate::dom::performance::reduce_timing_resolution; use crate::dom::performanceentry::PerformanceEntry; use dom_struct::dom_struct; use net_traits::ResourceFetchTiming; @@ -182,17 +182,17 @@ impl PerformanceResourceTimingMethods for PerformanceResourceTiming { // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-domainlookupstart fn DomainLookupStart(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.domain_lookup_start) + reduce_timing_resolution(self.domain_lookup_start) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-domainlookupend fn DomainLookupEnd(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.domain_lookup_end) + reduce_timing_resolution(self.domain_lookup_end) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-secureconnectionstart fn SecureConnectionStart(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.secure_connection_start) + reduce_timing_resolution(self.secure_connection_start) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-transfersize @@ -212,41 +212,41 @@ impl PerformanceResourceTimingMethods for PerformanceResourceTiming { // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-requeststart fn RequestStart(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.request_start) + reduce_timing_resolution(self.request_start) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-redirectstart fn RedirectStart(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.redirect_start) + reduce_timing_resolution(self.redirect_start) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-redirectend fn RedirectEnd(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.redirect_end) + reduce_timing_resolution(self.redirect_end) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responsestart fn ResponseStart(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.response_start) + reduce_timing_resolution(self.response_start) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-fetchstart fn FetchStart(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.fetch_start) + reduce_timing_resolution(self.fetch_start) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-connectstart fn ConnectStart(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.connect_start) + reduce_timing_resolution(self.connect_start) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-connectend fn ConnectEnd(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.connect_end) + reduce_timing_resolution(self.connect_end) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responseend fn ResponseEnd(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.response_end) + reduce_timing_resolution(self.response_end) } } diff --git a/components/script/dom/servoparser/prefetch.rs b/components/script/dom/servoparser/prefetch.rs index d9de4d7036f..c7468be7c19 100644 --- a/components/script/dom/servoparser/prefetch.rs +++ b/components/script/dom/servoparser/prefetch.rs @@ -128,7 +128,7 @@ impl TokenSink for PrefetchSink { self.origin.clone(), self.pipeline_id, self.get_cors_settings(tag, local_name!("crossorigin")), - self.get_referrer_policy(tag, LocalName::from("referrerpolicy")), + self.get_referrer_policy(tag, local_name!("referrerpolicy")), FromPictureOrSrcSet::No, ); let _ = self @@ -145,7 +145,7 @@ impl TokenSink for PrefetchSink { let cors_setting = self.get_cors_settings(tag, local_name!("crossorigin")); let referrer_policy = - self.get_referrer_policy(tag, LocalName::from("referrerpolicy")); + self.get_referrer_policy(tag, local_name!("referrerpolicy")); let integrity_metadata = self .get_attr(tag, local_name!("integrity")) .map(|attr| String::from(&attr.value)) diff --git a/components/script/dom/submitevent.rs b/components/script/dom/submitevent.rs new file mode 100644 index 00000000000..17f8e7fd003 --- /dev/null +++ b/components/script/dom/submitevent.rs @@ -0,0 +1,79 @@ +/* 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::EventBinding::EventMethods; +use crate::dom::bindings::codegen::Bindings::SubmitEventBinding; +use crate::dom::bindings::codegen::Bindings::SubmitEventBinding::SubmitEventMethods; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::event::Event; +use crate::dom::globalscope::GlobalScope; +use crate::dom::htmlelement::HTMLElement; +use crate::dom::window::Window; +use dom_struct::dom_struct; +use servo_atoms::Atom; + +#[dom_struct] +#[allow(non_snake_case)] +pub struct SubmitEvent { + event: Event, + submitter: Option<DomRoot<HTMLElement>>, +} + +impl SubmitEvent { + fn new_inherited(submitter: Option<DomRoot<HTMLElement>>) -> SubmitEvent { + SubmitEvent { + event: Event::new_inherited(), + submitter: submitter, + } + } + + pub fn new( + global: &GlobalScope, + type_: Atom, + bubbles: bool, + cancelable: bool, + submitter: Option<DomRoot<HTMLElement>>, + ) -> DomRoot<SubmitEvent> { + let ev = reflect_dom_object( + Box::new(SubmitEvent::new_inherited(submitter)), + global, + SubmitEventBinding::Wrap, + ); + { + let event = ev.upcast::<Event>(); + event.init_event(type_, bubbles, cancelable); + } + ev + } + + #[allow(non_snake_case)] + pub fn Constructor( + window: &Window, + type_: DOMString, + init: &SubmitEventBinding::SubmitEventInit, + ) -> DomRoot<SubmitEvent> { + SubmitEvent::new( + &window.global(), + Atom::from(type_), + init.parent.bubbles, + init.parent.cancelable, + init.submitter.as_ref().map(|s| DomRoot::from_ref(&**s)), + ) + } +} + +impl SubmitEventMethods for SubmitEvent { + /// <https://dom.spec.whatwg.org/#dom-event-istrusted> + fn IsTrusted(&self) -> bool { + self.event.IsTrusted() + } + + /// https://html.spec.whatwg.org/multipage/#dom-submitevent-submitter + fn GetSubmitter(&self) -> Option<DomRoot<HTMLElement>> { + self.submitter.as_ref().map(|s| DomRoot::from_ref(&**s)) + } +} diff --git a/components/script/dom/vrframedata.rs b/components/script/dom/vrframedata.rs index d4e92433f10..32af3b394ef 100644 --- a/components/script/dom/vrframedata.rs +++ b/components/script/dom/vrframedata.rs @@ -2,13 +2,14 @@ * 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::PerformanceBinding::DOMHighResTimeStamp; use crate::dom::bindings::codegen::Bindings::VRFrameDataBinding; use crate::dom::bindings::codegen::Bindings::VRFrameDataBinding::VRFrameDataMethods; use crate::dom::bindings::error::Fallible; -use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::globalscope::GlobalScope; +use crate::dom::performance::reduce_timing_resolution; use crate::dom::vrpose::VRPose; use crate::dom::window::Window; use crate::script_runtime::JSContext; @@ -119,8 +120,8 @@ impl VRFrameData { impl VRFrameDataMethods for VRFrameData { // https://w3c.github.io/webvr/#dom-vrframedata-timestamp - fn Timestamp(&self) -> Finite<f64> { - Finite::wrap(self.timestamp.get() - self.first_timestamp.get()) + fn Timestamp(&self) -> DOMHighResTimeStamp { + reduce_timing_resolution(self.timestamp.get() - self.first_timestamp.get()) } #[allow(unsafe_code)] diff --git a/components/script/dom/webidls/Blob.webidl b/components/script/dom/webidls/Blob.webidl index 15d28cadb99..8a3f5f5185e 100644 --- a/components/script/dom/webidls/Blob.webidl +++ b/components/script/dom/webidls/Blob.webidl @@ -16,6 +16,9 @@ interface Blob { Blob slice(optional [Clamp] long long start, optional [Clamp] long long end, optional DOMString contentType); + + [NewObject] Promise<DOMString> text(); + [NewObject] Promise<ArrayBuffer> arrayBuffer(); }; dictionary BlobPropertyBag { diff --git a/components/script/dom/webidls/CanvasRenderingContext2D.webidl b/components/script/dom/webidls/CanvasRenderingContext2D.webidl index 52e532e3a8b..bcc1b56396c 100644 --- a/components/script/dom/webidls/CanvasRenderingContext2D.webidl +++ b/components/script/dom/webidls/CanvasRenderingContext2D.webidl @@ -93,7 +93,7 @@ interface mixin CanvasFillStrokeStyles { [Throws] CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1); [Throws] - CanvasPattern createPattern(CanvasImageSource image, [TreatNullAs=EmptyString] DOMString repetition); + CanvasPattern? createPattern(CanvasImageSource image, [TreatNullAs=EmptyString] DOMString repetition); }; [Exposed=(PaintWorklet, Window, Worker)] diff --git a/components/script/dom/webidls/GPUBindGroup.webidl b/components/script/dom/webidls/GPUBindGroup.webidl new file mode 100644 index 00000000000..537fda29a63 --- /dev/null +++ b/components/script/dom/webidls/GPUBindGroup.webidl @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://gpuweb.github.io/gpuweb/#gpubindgrouplayout +[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"] +interface GPUBindGroup { +}; +GPUBindGroup includes GPUObjectBase; + +dictionary GPUBindGroupDescriptor : GPUObjectDescriptorBase { + required GPUBindGroupLayout layout; + required sequence<GPUBindGroupBindings> bindings; +}; + +typedef /*(GPUSampler or GPUTextureView or*/ GPUBufferBindings/*)*/ GPUBindingResource; + +// Note: Servo codegen doesn't like the name `GPUBindGroupBinding` because it's already occupied +// dictionary GPUBindGroupBinding { +dictionary GPUBindGroupBindings { + required unsigned long binding; + required GPUBindingResource resource; +}; + +// Note: Servo codegen doesn't like the name `GPUBufferBinding` because it's already occupied +// dictionary GPUBufferBinding { +dictionary GPUBufferBindings { + required GPUBuffer buffer; + GPUBufferSize offset = 0; + GPUBufferSize size; +}; diff --git a/components/script/dom/webidls/GPUCommandBuffer.webidl b/components/script/dom/webidls/GPUCommandBuffer.webidl new file mode 100644 index 00000000000..fc61ccf4d64 --- /dev/null +++ b/components/script/dom/webidls/GPUCommandBuffer.webidl @@ -0,0 +1,9 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://gpuweb.github.io/gpuweb/#gpucommandbuffer +[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"] +interface GPUCommandBuffer { +}; +GPUCommandBuffer includes GPUObjectBase; diff --git a/components/script/dom/webidls/GPUCommandEncoder.webidl b/components/script/dom/webidls/GPUCommandEncoder.webidl new file mode 100644 index 00000000000..481c720260f --- /dev/null +++ b/components/script/dom/webidls/GPUCommandEncoder.webidl @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://gpuweb.github.io/gpuweb/#gpucommandencoder +[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"] +interface GPUCommandEncoder { + // GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor); + GPUComputePassEncoder beginComputePass(optional GPUComputePassDescriptor descriptor = {}); + + void copyBufferToBuffer( + GPUBuffer source, + GPUBufferSize sourceOffset, + GPUBuffer destination, + GPUBufferSize destinationOffset, + GPUBufferSize size); + + // void copyBufferToTexture( + // GPUBufferCopyView source, + // GPUTextureCopyView destination, + // GPUExtent3D copySize); + + // void copyTextureToBuffer( + // GPUTextureCopyView source, + // GPUBufferCopyView destination, + // GPUExtent3D copySize); + + // void copyTextureToTexture( + // GPUTextureCopyView source, + // GPUTextureCopyView destination, + // GPUExtent3D copySize); + + // void pushDebugGroup(DOMString groupLabel); + // void popDebugGroup(); + // void insertDebugMarker(DOMString markerLabel); + + GPUCommandBuffer finish(optional GPUCommandBufferDescriptor descriptor = {}); +}; +GPUCommandEncoder includes GPUObjectBase; + +dictionary GPUComputePassDescriptor : GPUObjectDescriptorBase { +}; + +dictionary GPUCommandBufferDescriptor : GPUObjectDescriptorBase { +}; diff --git a/components/script/dom/webidls/GPUComputePassEncoder.webidl b/components/script/dom/webidls/GPUComputePassEncoder.webidl new file mode 100644 index 00000000000..0c03436a7e4 --- /dev/null +++ b/components/script/dom/webidls/GPUComputePassEncoder.webidl @@ -0,0 +1,15 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://gpuweb.github.io/gpuweb/#gpucomputepassencoder +[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"] +interface GPUComputePassEncoder { + // void setPipeline(GPUComputePipeline pipeline); + // void dispatch(unsigned long x, optional unsigned long y = 1, optional unsigned long z = 1); + // void dispatchIndirect(GPUBuffer indirectBuffer, GPUBufferSize indirectOffset); + + // void endPass(); +}; +GPUComputePassEncoder includes GPUObjectBase; +GPUComputePassEncoder includes GPUProgrammablePassEncoder; diff --git a/components/script/dom/webidls/GPUComputePipeline.webidl b/components/script/dom/webidls/GPUComputePipeline.webidl new file mode 100644 index 00000000000..671c36ad351 --- /dev/null +++ b/components/script/dom/webidls/GPUComputePipeline.webidl @@ -0,0 +1,22 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://gpuweb.github.io/gpuweb/#gpucomputepipeline +[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"] +interface GPUComputePipeline { +}; +GPUComputePipeline includes GPUObjectBase; + +dictionary GPUPipelineDescriptorBase : GPUObjectDescriptorBase { + required GPUPipelineLayout layout; +}; + +dictionary GPUProgrammableStageDescriptor { + required GPUShaderModule module; + required DOMString entryPoint; +}; + +dictionary GPUComputePipelineDescriptor : GPUPipelineDescriptorBase { + required GPUProgrammableStageDescriptor computeStage; +}; diff --git a/components/script/dom/webidls/GPUDevice.webidl b/components/script/dom/webidls/GPUDevice.webidl index 85e8a3ea633..05fb449df8c 100644 --- a/components/script/dom/webidls/GPUDevice.webidl +++ b/components/script/dom/webidls/GPUDevice.webidl @@ -11,21 +11,24 @@ interface GPUDevice : EventTarget { GPUBuffer createBuffer(GPUBufferDescriptor descriptor); GPUMappedBuffer createBufferMapped(GPUBufferDescriptor descriptor); - //Promise<GPUMappedBuffer> createBufferMappedAsync(GPUBufferDescriptor descriptor); - //GPUTexture createTexture(GPUTextureDescriptor descriptor); - //GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {}); + // Promise<GPUMappedBuffer> createBufferMappedAsync(GPUBufferDescriptor descriptor); + // GPUTexture createTexture(GPUTextureDescriptor descriptor); + // GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {}); GPUBindGroupLayout createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor); GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor); - /*GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor); + GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor); GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor); GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor); - GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor); + // GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor); GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {}); - GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor); + // GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor); - GPUQueue getQueue();*/ + // GPUQueue getQueue(); }; GPUDevice includes GPUObjectBase; + +dictionary GPUCommandEncoderDescriptor : GPUObjectDescriptorBase { +}; diff --git a/components/script/dom/webidls/GPUProgrammablePassEncoder.webidl b/components/script/dom/webidls/GPUProgrammablePassEncoder.webidl new file mode 100644 index 00000000000..2a44e806065 --- /dev/null +++ b/components/script/dom/webidls/GPUProgrammablePassEncoder.webidl @@ -0,0 +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/. */ + +// https://gpuweb.github.io/gpuweb/#gpuprogrammablepassencoder +[Exposed=(Window, DedicatedWorker)] +interface mixin GPUProgrammablePassEncoder { + // void setBindGroup(unsigned long index, GPUBindGroup bindGroup, + // optional sequence<unsigned long> dynamicOffsets = []); + + // void setBindGroup(unsigned long index, GPUBindGroup bindGroup, + // Uint32Array dynamicOffsetsData, + // unsigned long long dynamicOffsetsDataStart, + // unsigned long long dynamicOffsetsDataLength); + + // void pushDebugGroup(DOMString groupLabel); + // void popDebugGroup(); + // void insertDebugMarker(DOMString markerLabel); +}; diff --git a/components/script/dom/webidls/GPUShaderModule.webidl b/components/script/dom/webidls/GPUShaderModule.webidl new file mode 100644 index 00000000000..0fdfc7c0327 --- /dev/null +++ b/components/script/dom/webidls/GPUShaderModule.webidl @@ -0,0 +1,15 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://gpuweb.github.io/gpuweb/#gpushadermodule +[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"] +interface GPUShaderModule { +}; +GPUShaderModule includes GPUObjectBase; + +typedef (Uint32Array or DOMString) GPUShaderCode; + +dictionary GPUShaderModuleDescriptor : GPUObjectDescriptorBase { + required GPUShaderCode code; +}; diff --git a/components/script/dom/webidls/SubmitEvent.webidl b/components/script/dom/webidls/SubmitEvent.webidl new file mode 100644 index 00000000000..f5b2c49257d --- /dev/null +++ b/components/script/dom/webidls/SubmitEvent.webidl @@ -0,0 +1,15 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://html.spec.whatwg.org/multipage/#submitevent +[Exposed=Window] +interface SubmitEvent : Event { + constructor(DOMString typeArg, optional SubmitEventInit eventInitDict = {}); + + readonly attribute HTMLElement? submitter; +}; + +dictionary SubmitEventInit : EventInit { + HTMLElement? submitter = null; +}; diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl index afd5183de33..eb8e4232886 100644 --- a/components/script/dom/webidls/Window.webidl +++ b/components/script/dom/webidls/Window.webidl @@ -55,8 +55,8 @@ // user prompts void alert(DOMString message); void alert(); - //boolean confirm(optional DOMString message = ""); - //DOMString? prompt(optional DOMString message = "", optional DOMString default = ""); + boolean confirm(optional DOMString message = ""); + DOMString? prompt(optional DOMString message = "", optional DOMString default = ""); //void print(); //any showModalDialog(DOMString url, optional any argument); diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index dc30017e7d1..208d7f1d87e 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -74,7 +74,7 @@ use crossbeam_channel::{unbounded, Sender, TryRecvError}; use cssparser::{Parser, ParserInput, SourceLocation}; use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType}; use dom_struct::dom_struct; -use embedder_traits::{EmbedderMsg, EventLoopWaker}; +use embedder_traits::{EmbedderMsg, EventLoopWaker, PromptDefinition, PromptOrigin, PromptResult}; use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect}; use euclid::{Point2D, Rect, Scale, Size2D, Vector2D}; use ipc_channel::ipc::{channel, IpcSender}; @@ -620,11 +620,32 @@ impl WindowMethods for Window { } let (sender, receiver) = ProfiledIpc::channel(self.global().time_profiler_chan().clone()).unwrap(); - let msg = EmbedderMsg::Alert(s.to_string(), sender); + let prompt = PromptDefinition::Alert(s.to_string(), sender); + let msg = EmbedderMsg::Prompt(prompt, PromptOrigin::Untrusted); self.send_to_embedder(msg); receiver.recv().unwrap(); } + // https://html.spec.whatwg.org/multipage/#dom-confirm + fn Confirm(&self, s: DOMString) -> bool { + let (sender, receiver) = + ProfiledIpc::channel(self.global().time_profiler_chan().clone()).unwrap(); + let prompt = PromptDefinition::OkCancel(s.to_string(), sender); + let msg = EmbedderMsg::Prompt(prompt, PromptOrigin::Untrusted); + self.send_to_embedder(msg); + receiver.recv().unwrap() == PromptResult::Primary + } + + // https://html.spec.whatwg.org/multipage/#dom-prompt + fn Prompt(&self, message: DOMString, default: DOMString) -> Option<DOMString> { + let (sender, receiver) = + ProfiledIpc::channel(self.global().time_profiler_chan().clone()).unwrap(); + let prompt = PromptDefinition::Input(message.to_string(), default.to_string(), sender); + let msg = EmbedderMsg::Prompt(prompt, PromptOrigin::Untrusted); + self.send_to_embedder(msg); + receiver.recv().unwrap().map(|s| s.into()) + } + // https://html.spec.whatwg.org/multipage/#dom-window-stop fn Stop(&self) { // TODO: Cancel ongoing navigation. @@ -1763,7 +1784,7 @@ impl Window { } pub fn client_rect_query(&self, node: &Node) -> UntypedRect<i32> { - if !self.layout_reflow(QueryMsg::NodeGeometryQuery(node.to_opaque())) { + if !self.layout_reflow(QueryMsg::ClientRectQuery(node.to_opaque())) { return Rect::zero(); } self.layout_rpc.node_geometry().client_rect @@ -2351,7 +2372,7 @@ fn debug_reflow_events(id: PipelineId, reflow_goal: &ReflowGoal, reason: &Reflow &QueryMsg::ContentBoxQuery(_n) => "\tContentBoxQuery", &QueryMsg::ContentBoxesQuery(_n) => "\tContentBoxesQuery", &QueryMsg::NodesFromPointQuery(..) => "\tNodesFromPointQuery", - &QueryMsg::NodeGeometryQuery(_n) => "\tNodeGeometryQuery", + &QueryMsg::ClientRectQuery(_n) => "\tClientRectQuery", &QueryMsg::NodeScrollGeometryQuery(_n) => "\tNodeScrollGeometryQuery", &QueryMsg::NodeScrollIdQuery(_n) => "\tNodeScrollIdQuery", &QueryMsg::ResolvedStyleQuery(_, _, _) => "\tResolvedStyleQuery", diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 08b37c946c5..6c531e5b4d6 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -618,7 +618,7 @@ impl XMLHttpRequestMethods for XMLHttpRequest { } // Substep 2 if !self.upload_complete.get() { - self.dispatch_upload_progress_event(atom!("loadstart"), Some(0)); + self.dispatch_upload_progress_event(atom!("loadstart"), Ok(Some(0))); if self.generation_id.get() != gen_id { return Ok(()); } @@ -1062,11 +1062,11 @@ impl XMLHttpRequest { self.upload_complete.set(true); // Substeps 2-4 if !self.sync.get() { - self.dispatch_upload_progress_event(atom!("progress"), None); + self.dispatch_upload_progress_event(atom!("progress"), Ok(None)); return_if_fetch_was_terminated!(); - self.dispatch_upload_progress_event(atom!("load"), None); + self.dispatch_upload_progress_event(atom!("load"), Ok(None)); return_if_fetch_was_terminated!(); - self.dispatch_upload_progress_event(atom!("loadend"), None); + self.dispatch_upload_progress_event(atom!("loadend"), Ok(None)); return_if_fetch_was_terminated!(); } // Part of step 13, send() (processing response) @@ -1164,9 +1164,9 @@ impl XMLHttpRequest { let upload_complete = &self.upload_complete; if !upload_complete.get() { upload_complete.set(true); - self.dispatch_upload_progress_event(Atom::from(errormsg), None); + self.dispatch_upload_progress_event(Atom::from(errormsg), Err(())); return_if_fetch_was_terminated!(); - self.dispatch_upload_progress_event(atom!("loadend"), None); + self.dispatch_upload_progress_event(atom!("loadend"), Err(())); return_if_fetch_was_terminated!(); } self.dispatch_response_progress_event(Atom::from(errormsg)); @@ -1210,11 +1210,19 @@ impl XMLHttpRequest { progressevent.upcast::<Event>().fire(target); } - fn dispatch_upload_progress_event(&self, type_: Atom, partial_load: Option<u64>) { - // If partial_load is None, loading has completed and we can just use the value from the request body + fn dispatch_upload_progress_event(&self, type_: Atom, partial_load: Result<Option<u64>, ()>) { + // If partial_load is Ok(None), loading has completed and we can just use the value from the request body + // If an error occured, we pass 0 for both loaded and total - let total = self.request_body_len.get() as u64; - self.dispatch_progress_event(true, type_, partial_load.unwrap_or(total), Some(total)); + let request_body_len = self.request_body_len.get() as u64; + let (loaded, total) = match partial_load { + Ok(l) => match l { + Some(loaded) => (loaded, Some(request_body_len)), + None => (request_body_len, Some(request_body_len)), + }, + Err(()) => (0, None), + }; + self.dispatch_progress_event(true, type_, loaded, total); } fn dispatch_response_progress_event(&self, type_: Atom) { diff --git a/components/script/dom/xrsession.rs b/components/script/dom/xrsession.rs index 4814eb379ff..68fe5a442fd 100644 --- a/components/script/dom/xrsession.rs +++ b/components/script/dom/xrsession.rs @@ -19,7 +19,6 @@ use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRVisibilityState use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerMethods; use crate::dom::bindings::error::{Error, ErrorResult}; use crate::dom::bindings::inheritance::Castable; -use crate::dom::bindings::num::Finite; use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::{Dom, DomRoot, MutDom, MutNullableDom}; @@ -28,6 +27,7 @@ use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::node::Node; use crate::dom::node::NodeDamage; +use crate::dom::performance::reduce_timing_resolution; use crate::dom::promise::Promise; use crate::dom::xrframe::XRFrame; use crate::dom::xrinputsourcearray::XRInputSourceArray; @@ -336,7 +336,7 @@ impl XRSession { // Step 4-5 let mut callbacks = mem::replace(&mut *self.raf_callback_list.borrow_mut(), vec![]); let start = self.global().as_window().get_navigation_start(); - let time = (frame.time_ns - start).to_ms(); + let time = reduce_timing_resolution((frame.time_ns - start).to_ms()); let frame = XRFrame::new(&self.global(), self, frame); // Step 6,7 @@ -347,7 +347,7 @@ impl XRSession { self.outside_raf.set(false); for (_, callback) in callbacks.drain(..) { if let Some(callback) = callback { - let _ = callback.Call__(Finite::wrap(time), &frame, ExceptionHandling::Report); + let _ = callback.Call__(time, &frame, ExceptionHandling::Report); } } self.outside_raf.set(true); |