diff options
Diffstat (limited to 'components/script/dom')
40 files changed, 1862 insertions, 414 deletions
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index e0a5df68882..d79bc073e76 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -5147,8 +5147,7 @@ class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod): attrs = "JSPROP_ENUMERATE" if self.descriptor.operations['IndexedSetter'] is None: attrs += " | JSPROP_READONLY" - # FIXME(#11868) Should assign to desc.value, desc.get() is a copy. - fillDescriptor = ("desc.get().value = result_root.get();\n" + fillDescriptor = ("desc.value = result_root.get();\n" "fill_property_descriptor(MutableHandle::from_raw(desc), proxy.get(), (%s) as u32);\n" "return true;" % attrs) templateValues = { @@ -5173,8 +5172,7 @@ class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod): attrs = " | ".join(attrs) else: attrs = "0" - # FIXME(#11868) Should assign to desc.value, desc.get() is a copy. - fillDescriptor = ("desc.get().value = result_root.get();\n" + fillDescriptor = ("desc.value = result_root.get();\n" "fill_property_descriptor(MutableHandle::from_raw(desc), proxy.get(), (%s) as u32);\n" "return true;" % attrs) templateValues = { @@ -5221,7 +5219,7 @@ if !expando.is_null() { } } """ + namedGet + """\ -desc.get().obj = ptr::null_mut(); +desc.obj = ptr::null_mut(); return true;""" def definition_body(self): diff --git a/components/script/dom/bindings/str.rs b/components/script/dom/bindings/str.rs index 2b4830dccfc..67ba6f34b3d 100644 --- a/components/script/dom/bindings/str.rs +++ b/components/script/dom/bindings/str.rs @@ -294,7 +294,7 @@ impl DOMString { // Step 4.3.1 "." State::MilliStop => next_state(c == '.', State::MilliHigh), // Step 4.3.2 "SSS" - State::MilliHigh => next_state(c.is_digit(6), State::MilliMiddle), + State::MilliHigh => next_state(c.is_digit(10), State::MilliMiddle), State::MilliMiddle => next_state(c.is_digit(10), State::MilliLow), State::MilliLow => next_state(c.is_digit(10), State::Done), @@ -318,21 +318,108 @@ impl DOMString { /// YYYY must be four or more digits, MM and DD both must be two digits /// https://html.spec.whatwg.org/multipage/#valid-date-string pub fn is_valid_date_string(&self) -> bool { - parse_date_string(&self.0).is_ok() + self.parse_date_string().is_ok() + } + + /// https://html.spec.whatwg.org/multipage/#parse-a-date-string + pub fn parse_date_string(&self) -> Result<(i32, u32, u32), ()> { + let value = &self.0; + // Step 1, 2, 3 + let (year_int, month_int, day_int) = parse_date_component(value)?; + + // Step 4 + if value.split('-').nth(3).is_some() { + return Err(()); + } + + // Step 5, 6 + Ok((year_int, month_int, day_int)) + } + + /// https://html.spec.whatwg.org/multipage/#parse-a-time-string + pub fn parse_time_string(&self) -> Result<(u32, u32, f64), ()> { + let value = &self.0; + // Step 1, 2, 3 + let (hour_int, minute_int, second_float) = parse_time_component(value)?; + + // Step 4 + if value.split(':').nth(3).is_some() { + return Err(()); + } + + // Step 5, 6 + Ok((hour_int, minute_int, second_float)) } /// A valid month string should be "YYYY-MM" /// YYYY must be four or more digits, MM both must be two digits /// https://html.spec.whatwg.org/multipage/#valid-month-string pub fn is_valid_month_string(&self) -> bool { - parse_month_string(&self.0).is_ok() + self.parse_month_string().is_ok() + } + + /// https://html.spec.whatwg.org/multipage/#parse-a-month-string + pub fn parse_month_string(&self) -> Result<(i32, u32), ()> { + let value = &self; + // Step 1, 2, 3 + let (year_int, month_int) = parse_month_component(value)?; + + // Step 4 + if value.split("-").nth(2).is_some() { + return Err(()); + } + // Step 5 + Ok((year_int, month_int)) } /// A valid week string should be like {YYYY}-W{WW}, such as "2017-W52" /// YYYY must be four or more digits, WW both must be two digits /// https://html.spec.whatwg.org/multipage/#valid-week-string pub fn is_valid_week_string(&self) -> bool { - parse_week_string(&self.0).is_ok() + self.parse_week_string().is_ok() + } + + /// https://html.spec.whatwg.org/multipage/#parse-a-week-string + pub fn parse_week_string(&self) -> Result<(i32, u32), ()> { + let value = &self.0; + // Step 1, 2, 3 + let mut iterator = value.split('-'); + let year = iterator.next().ok_or(())?; + + // Step 4 + let year_int = year.parse::<i32>().map_err(|_| ())?; + if year.len() < 4 || year_int == 0 { + return Err(()); + } + + // Step 5, 6 + let week = iterator.next().ok_or(())?; + let (week_first, week_last) = week.split_at(1); + if week_first != "W" { + return Err(()); + } + + // Step 7 + let week_int = week_last.parse::<u32>().map_err(|_| ())?; + if week_last.len() != 2 { + return Err(()); + } + + // Step 8 + let max_week = max_week_in_year(year_int); + + // Step 9 + if week_int < 1 || week_int > max_week { + return Err(()); + } + + // Step 10 + if iterator.next().is_some() { + return Err(()); + } + + // Step 11 + Ok((year_int, week_int)) } /// https://html.spec.whatwg.org/multipage/#valid-floating-point-number @@ -341,12 +428,37 @@ impl DOMString { static ref RE: Regex = Regex::new(r"^-?(?:\d+\.\d+|\d+|\.\d+)(?:(e|E)(\+|\-)?\d+)?$").unwrap(); } - RE.is_match(&self.0) && parse_floating_point_number(&self.0).is_ok() + RE.is_match(&self.0) && self.parse_floating_point_number().is_ok() + } + + /// https://html.spec.whatwg.org/multipage/#rules-for-parsing-floating-point-number-values + pub fn parse_floating_point_number(&self) -> Result<f64, ()> { + // Steps 15-16 are telling us things about IEEE rounding modes + // for floating-point significands; this code assumes the Rust + // compiler already matches them in any cases where + // that actually matters. They are not + // related to f64::round(), which is for rounding to integers. + let input = &self.0; + match input.trim().parse::<f64>() { + Ok(val) + if !( + // A valid number is the same as what rust considers to be valid, + // except for +1., NaN, and Infinity. + val.is_infinite() || + val.is_nan() || + input.ends_with(".") || + input.starts_with("+") + ) => + { + Ok(val) + }, + _ => Err(()), + } } /// https://html.spec.whatwg.org/multipage/#best-representation-of-the-number-as-a-floating-point-number pub fn set_best_representation_of_the_floating_point_number(&mut self) { - if let Ok(val) = parse_floating_point_number(&self.0) { + if let Ok(val) = self.parse_floating_point_number() { self.0 = val.to_string(); } } @@ -356,7 +468,7 @@ impl DOMString { /// https://html.spec.whatwg.org/multipage/#valid-normalised-local-date-and-time-string pub fn convert_valid_normalized_local_date_and_time_string(&mut self) -> Result<(), ()> { let ((year, month, day), (hour, minute, second)) = - parse_local_date_and_time_string(&*self.0)?; + self.parse_local_date_and_time_string()?; if second == 0.0 { self.0 = format!( "{:04}-{:02}-{:02}T{:02}:{:02}", @@ -370,6 +482,35 @@ impl DOMString { } Ok(()) } + + /// https://html.spec.whatwg.org/multipage/#parse-a-local-date-and-time-string + pub fn parse_local_date_and_time_string( + &self, + ) -> Result<((i32, u32, u32), (u32, u32, f64)), ()> { + let value = &self; + // Step 1, 2, 4 + let mut iterator = if value.contains('T') { + value.split('T') + } else { + value.split(' ') + }; + + // Step 3 + let date = iterator.next().ok_or(())?; + let date_tuple = parse_date_component(date)?; + + // Step 5 + let time = iterator.next().ok_or(())?; + let time_tuple = parse_time_component(time)?; + + // Step 6 + if iterator.next().is_some() { + return Err(()); + } + + // Step 7, 8, 9 + Ok((date_tuple, time_tuple)) + } } impl Borrow<str> for DOMString { @@ -498,84 +639,15 @@ impl Extend<char> for DOMString { } } -/// https://html.spec.whatwg.org/multipage/#parse-a-month-string -fn parse_month_string(value: &str) -> Result<(u32, u32), ()> { - // Step 1, 2, 3 - let (year_int, month_int) = parse_month_component(value)?; - - // Step 4 - if value.split("-").nth(2).is_some() { - return Err(()); - } - // Step 5 - Ok((year_int, month_int)) -} - -/// https://html.spec.whatwg.org/multipage/#parse-a-date-string -fn parse_date_string(value: &str) -> Result<(u32, u32, u32), ()> { - // Step 1, 2, 3 - let (year_int, month_int, day_int) = parse_date_component(value)?; - - // Step 4 - if value.split('-').nth(3).is_some() { - return Err(()); - } - - // Step 5, 6 - Ok((year_int, month_int, day_int)) -} - -/// https://html.spec.whatwg.org/multipage/#parse-a-week-string -fn parse_week_string(value: &str) -> Result<(u32, u32), ()> { - // Step 1, 2, 3 - let mut iterator = value.split('-'); - let year = iterator.next().ok_or(())?; - - // Step 4 - let year_int = year.parse::<u32>().map_err(|_| ())?; - if year.len() < 4 || year_int == 0 { - return Err(()); - } - - // Step 5, 6 - let week = iterator.next().ok_or(())?; - let (week_first, week_last) = week.split_at(1); - if week_first != "W" { - return Err(()); - } - - // Step 7 - let week_int = week_last.parse::<u32>().map_err(|_| ())?; - if week_last.len() != 2 { - return Err(()); - } - - // Step 8 - let max_week = max_week_in_year(year_int); - - // Step 9 - if week_int < 1 || week_int > max_week { - return Err(()); - } - - // Step 10 - if iterator.next().is_some() { - return Err(()); - } - - // Step 11 - Ok((year_int, week_int)) -} - /// https://html.spec.whatwg.org/multipage/#parse-a-month-component -fn parse_month_component(value: &str) -> Result<(u32, u32), ()> { +fn parse_month_component(value: &str) -> Result<(i32, u32), ()> { // Step 3 let mut iterator = value.split('-'); let year = iterator.next().ok_or(())?; let month = iterator.next().ok_or(())?; // Step 1, 2 - let year_int = year.parse::<u32>().map_err(|_| ())?; + let year_int = year.parse::<i32>().map_err(|_| ())?; if year.len() < 4 || year_int == 0 { return Err(()); } @@ -591,7 +663,7 @@ fn parse_month_component(value: &str) -> Result<(u32, u32), ()> { } /// https://html.spec.whatwg.org/multipage/#parse-a-date-component -fn parse_date_component(value: &str) -> Result<(u32, u32, u32), ()> { +fn parse_date_component(value: &str) -> Result<(i32, u32, u32), ()> { // Step 1 let (year_int, month_int) = parse_month_component(value)?; @@ -613,7 +685,7 @@ fn parse_date_component(value: &str) -> Result<(u32, u32, u32), ()> { } /// https://html.spec.whatwg.org/multipage/#parse-a-time-component -fn parse_time_component(value: &str) -> Result<(u32, u32, f32), ()> { +fn parse_time_component(value: &str) -> Result<(u32, u32, f64), ()> { // Step 1 let mut iterator = value.split(':'); let hour = iterator.next().ok_or(())?; @@ -655,7 +727,7 @@ fn parse_time_component(value: &str) -> Result<(u32, u32, f32), ()> { None => {}, } - second.parse::<f32>().map_err(|_| ())? + second.parse::<f64>().map_err(|_| ())? }, None => 0.0, }; @@ -664,33 +736,7 @@ fn parse_time_component(value: &str) -> Result<(u32, u32, f32), ()> { Ok((hour_int, minute_int, second_float)) } -/// https://html.spec.whatwg.org/multipage/#parse-a-local-date-and-time-string -fn parse_local_date_and_time_string(value: &str) -> Result<((u32, u32, u32), (u32, u32, f32)), ()> { - // Step 1, 2, 4 - let mut iterator = if value.contains('T') { - value.split('T') - } else { - value.split(' ') - }; - - // Step 3 - let date = iterator.next().ok_or(())?; - let date_tuple = parse_date_component(date)?; - - // Step 5 - let time = iterator.next().ok_or(())?; - let time_tuple = parse_time_component(time)?; - - // Step 6 - if iterator.next().is_some() { - return Err(()); - } - - // Step 7, 8, 9 - Ok((date_tuple, time_tuple)) -} - -fn max_day_in_month(year_num: u32, month_num: u32) -> Result<u32, ()> { +fn max_day_in_month(year_num: i32, month_num: u32) -> Result<u32, ()> { match month_num { 1 | 3 | 5 | 7 | 8 | 10 | 12 => Ok(31), 4 | 6 | 9 | 11 => Ok(30), @@ -706,7 +752,7 @@ fn max_day_in_month(year_num: u32, month_num: u32) -> Result<u32, ()> { } /// https://html.spec.whatwg.org/multipage/#week-number-of-the-last-day -fn max_week_in_year(year: u32) -> u32 { +fn max_week_in_year(year: i32) -> u32 { match Utc.ymd(year as i32, 1, 1).weekday() { Weekday::Thu => 53, Weekday::Wed if is_leap_year(year) => 53, @@ -715,23 +761,6 @@ fn max_week_in_year(year: u32) -> u32 { } #[inline] -fn is_leap_year(year: u32) -> bool { +fn is_leap_year(year: i32) -> bool { year % 400 == 0 || (year % 4 == 0 && year % 100 != 0) } - -/// https://html.spec.whatwg.org/multipage/#rules-for-parsing-floating-point-number-values -fn parse_floating_point_number(input: &str) -> Result<f64, ()> { - match input.trim().parse::<f64>() { - Ok(val) - if !( - // A valid number is the same as what rust considers to be valid, - // except for +1., NaN, and Infinity. - val.is_infinite() || val.is_nan() || input.ends_with(".") || input.starts_with("+") - ) => - { - // TODO(#19773): need consider `min`, `max`, `step`, when they are implemented - Ok(val.round()) - } - _ => Err(()), - } -} diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 1d83fff920d..bdcf9919752 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -151,7 +151,7 @@ use tendril::stream::LossyDecoder; use tendril::{StrTendril, TendrilSink}; use time::{Duration, Timespec, Tm}; use uuid::Uuid; -use webgpu::{WebGPU, WebGPUAdapter, WebGPUBuffer, WebGPUDevice}; +use webgpu::{WebGPU, WebGPUAdapter, WebGPUBindGroupLayout, WebGPUBuffer, WebGPUDevice}; use webrender_api::{DocumentId, ImageKey}; use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState}; use webxr_api::SwapChainId as WebXRSwapChainId; @@ -524,11 +524,13 @@ unsafe_no_jsmanaged_fields!(WebGLTextureId); unsafe_no_jsmanaged_fields!(WebGLVertexArrayId); unsafe_no_jsmanaged_fields!(WebGLVersion); unsafe_no_jsmanaged_fields!(WebGLSLVersion); +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!(WebGPUBindGroupLayout); unsafe_no_jsmanaged_fields!(GPUBufferState); unsafe_no_jsmanaged_fields!(WebXRSwapChainId); unsafe_no_jsmanaged_fields!(MediaList); diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index e344e9ae279..56b2112e498 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -10,10 +10,8 @@ use crate::dom::bindings::codegen::PrototypeList::{MAX_PROTO_CHAIN_LENGTH, PROTO use crate::dom::bindings::conversions::{jsstring_to_str, private_from_proto_check}; use crate::dom::bindings::error::throw_invalid_this; use crate::dom::bindings::inheritance::TopTypeId; -use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::trace::trace_object; -use crate::dom::messageport::MessagePort; use crate::dom::windowproxy; use crate::script_runtime::JSContext as SafeJSContext; use js::conversions::{jsstr_to_string, ToJSValConvertible}; @@ -126,12 +124,9 @@ impl Clone for DOMJSClass { unsafe impl Sync for DOMJSClass {} /// Returns a JSVal representing a frozen array of ports -pub fn message_ports_to_frozen_array( - message_ports: &[DomRoot<MessagePort>], - cx: SafeJSContext, -) -> JSVal { +pub fn to_frozen_array<T: ToJSValConvertible>(convertibles: &[T], cx: SafeJSContext) -> JSVal { rooted!(in(*cx) let mut ports = UndefinedValue()); - unsafe { message_ports.to_jsval(*cx, ports.handle_mut()) }; + unsafe { convertibles.to_jsval(*cx, ports.handle_mut()) }; rooted!(in(*cx) let obj = ports.to_object()); unsafe { JS_FreezeObject(*cx, RawHandleObject::from(obj.handle())) }; diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs index ea259a6d2d7..cb42539a8e5 100644 --- a/components/script/dom/eventtarget.rs +++ b/components/script/dom/eventtarget.rs @@ -30,6 +30,7 @@ use crate::dom::element::Element; use crate::dom::errorevent::ErrorEvent; use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; use crate::dom::globalscope::GlobalScope; +use crate::dom::htmlformelement::FormControlElementHelpers; use crate::dom::node::document_from_node; use crate::dom::virtualmethods::VirtualMethods; use crate::dom::window::Window; @@ -452,35 +453,50 @@ impl EventTarget { } // https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler + // step 3 #[allow(unsafe_code)] fn get_compiled_event_handler( &self, handler: InternalRawUncompiledHandler, ty: &Atom, ) -> Option<CommonEventHandler> { - // Step 1.1 + // Step 3.1 let element = self.downcast::<Element>(); let document = match element { Some(element) => document_from_node(element), None => self.downcast::<Window>().unwrap().Document(), }; - // Step 1.2 + // Step 3.2 if !document.is_scripting_enabled() { return None; } - // Step 1.3 + // Step 3.3 let body: Vec<u16> = handler.source.encode_utf16().collect(); - // TODO step 1.5 (form owner) + // Step 3.4 is handler.line - // Step 1.6 + // Step 3.5 + let form_owner = element + .and_then(|e| e.as_maybe_form_control()) + .and_then(|f| f.form_owner()); + + // Step 3.6 TODO: settings objects not implemented + + // Step 3.7 is written as though we call the parser separately + // from the compiler; since we just call CompileFunction with + // source text, we handle parse errors later + + // Step 3.8 TODO: settings objects not implemented + + // Step 3.9 let window = document.window(); let url_serialized = CString::new(handler.url.to_string()).unwrap(); let name = CString::new(&**ty).unwrap(); + // Step 3.9, subsection ParameterList static mut ARG_NAMES: [*const c_char; 1] = [b"event\0" as *const u8 as *const c_char]; static mut ERROR_ARG_NAMES: [*const c_char; 5] = [ b"event\0" as *const u8 as *const c_char, @@ -489,7 +505,6 @@ impl EventTarget { b"colno\0" as *const u8 as *const c_char, b"error\0" as *const u8 as *const c_char, ]; - // step 10 let is_error = ty == &atom!("error") && self.is::<Window>(); let args = unsafe { if is_error { @@ -501,11 +516,19 @@ impl EventTarget { let cx = window.get_cx(); let options = CompileOptionsWrapper::new(*cx, url_serialized.as_ptr(), handler.line as u32); - // TODO step 1.10.1-3 (document, form owner, element in scope chain) + // Step 3.9, subsection Scope steps 1-6 let scopechain = AutoObjectVectorWrapper::new(*cx); - let _ac = enter_realm(&*window); + if let Some(element) = element { + scopechain.append(document.reflector().get_jsobject().get()); + if let Some(form_owner) = form_owner { + scopechain.append(form_owner.reflector().get_jsobject().get()); + } + scopechain.append(element.reflector().get_jsobject().get()); + } + + let _ac = enter_realm(&*window); // TODO 3.8 should replace this rooted!(in(*cx) let mut handler = ptr::null_mut::<JSFunction>()); let rv = unsafe { CompileFunction( @@ -525,17 +548,20 @@ impl EventTarget { ) }; if !rv || handler.get().is_null() { - // Step 1.8.2 + // Step 3.7 unsafe { let _ac = JSAutoRealm::new(*cx, self.reflector().get_jsobject().get()); // FIXME(#13152): dispatch error event. report_pending_exception(*cx, false); } - // Step 1.8.1 / 1.8.3 return None; } - // TODO step 1.11-13 + // Step 3.10 happens when we drop _ac + + // TODO Step 3.11 + + // Step 3.12 let funobj = unsafe { JS_GetFunctionObject(handler.get()) }; assert!(!funobj.is_null()); // Step 1.14 diff --git a/components/script/dom/extendablemessageevent.rs b/components/script/dom/extendablemessageevent.rs index 6131363fd99..a1433ec6269 100644 --- a/components/script/dom/extendablemessageevent.rs +++ b/components/script/dom/extendablemessageevent.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::ExtendableMessageEventBinding; use crate::dom::bindings::codegen::Bindings::ExtendableMessageEventBinding::ExtendableMessageEventMethods; use crate::dom::bindings::error::Fallible; @@ -10,7 +11,7 @@ use crate::dom::bindings::reflector::reflect_dom_object; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::trace::RootedTraceableBox; -use crate::dom::bindings::utils::message_ports_to_frozen_array; +use crate::dom::bindings::utils::to_frozen_array; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::extendableevent::ExtendableEvent; @@ -32,6 +33,8 @@ pub struct ExtendableMessageEvent { origin: DOMString, lastEventId: DOMString, ports: Vec<Dom<MessagePort>>, + #[ignore_malloc_size_of = "mozjs"] + frozen_ports: DomRefCell<Option<Heap<JSVal>>>, } impl ExtendableMessageEvent { @@ -49,6 +52,7 @@ impl ExtendableMessageEvent { .into_iter() .map(|port| Dom::from_ref(&*port)) .collect(), + frozen_ports: DomRefCell::new(None), } } @@ -141,11 +145,22 @@ impl ExtendableMessageEventMethods for ExtendableMessageEvent { /// https://w3c.github.io/ServiceWorker/#extendablemessage-event-ports fn Ports(&self, cx: JSContext) -> JSVal { + if let Some(ports) = &*self.frozen_ports.borrow() { + return ports.get(); + } + let ports: Vec<DomRoot<MessagePort>> = self .ports .iter() .map(|port| DomRoot::from_ref(&**port)) .collect(); - message_ports_to_frozen_array(ports.as_slice(), cx) + let frozen_ports = to_frozen_array(ports.as_slice(), cx); + + // Cache the Js value. + let heap_val = Heap::default(); + heap_val.set(frozen_ports); + *self.frozen_ports.borrow_mut() = Some(heap_val); + + frozen_ports } } diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 04f8d948a24..e551009b59d 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -4,6 +4,7 @@ use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::EventSourceBinding::EventSourceBinding::EventSourceMethods; +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}; @@ -32,7 +33,7 @@ use crate::dom::performance::Performance; use crate::dom::window::Window; use crate::dom::workerglobalscope::WorkerGlobalScope; use crate::dom::workletglobalscope::WorkletGlobalScope; -use crate::microtask::{Microtask, MicrotaskQueue}; +use crate::microtask::{Microtask, MicrotaskQueue, UserMicrotask}; use crate::script_module::ModuleTree; use crate::script_runtime::{CommonScriptMsg, JSContext as SafeJSContext, ScriptChan, ScriptPort}; use crate::script_thread::{MainThreadScriptChan, ScriptThread}; @@ -1772,6 +1773,13 @@ impl GlobalScope { self.timers.clear_timeout_or_interval(self, handle); } + pub fn queue_function_as_microtask(&self, callback: Rc<VoidFunction>) { + self.enqueue_microtask(Microtask::User(UserMicrotask { + callback: callback, + pipeline: self.pipeline_id(), + })) + } + pub fn fire_timer(&self, handle: TimerEventId) { self.timers.fire_timer(handle, self); } diff --git a/components/script/dom/gpu.rs b/components/script/dom/gpu.rs index 8a3e05f2d79..65f9cdeb0f8 100644 --- a/components/script/dom/gpu.rs +++ b/components/script/dom/gpu.rs @@ -19,9 +19,10 @@ use dom_struct::dom_struct; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use js::jsapi::Heap; +use script_traits::ScriptMsg; use std::rc::Rc; use webgpu::wgpu; -use webgpu::{WebGPU, WebGPURequest, WebGPUResponse, WebGPUResponseResult}; +use webgpu::{WebGPUResponse, WebGPUResponseResult}; #[dom_struct] pub struct GPU { @@ -38,10 +39,6 @@ impl GPU { pub fn new(global: &GlobalScope) -> DomRoot<GPU> { reflect_dom_object(Box::new(GPU::new_inherited()), global, GPUBinding::Wrap) } - - fn wgpu_channel(&self) -> Option<WebGPU> { - self.global().as_window().webgpu_channel() - } } pub trait AsyncWGPUListener { @@ -114,7 +111,8 @@ impl GPUMethods for GPU { options: &GPURequestAdapterOptions, comp: InCompartment, ) -> Rc<Promise> { - let promise = Promise::new_in_current_compartment(&self.global(), comp); + let global = &self.global(); + let promise = Promise::new_in_current_compartment(global, comp); let sender = response_async(&promise, self); let power_preference = match options.powerPreference { Some(GPUPowerPreference::Low_power) => wgpu::instance::PowerPreference::LowPower, @@ -123,21 +121,19 @@ impl GPUMethods for GPU { }, None => wgpu::instance::PowerPreference::Default, }; - let ids = self.global().as_window().Navigator().create_adapter_ids(); + let ids = global.as_window().Navigator().create_adapter_ids(); - match self.wgpu_channel() { - Some(channel) => { - channel - .0 - .send(WebGPURequest::RequestAdapter( - sender, - wgpu::instance::RequestAdapterOptions { power_preference }, - ids, - )) - .unwrap(); - }, - None => promise.reject_error(Error::Type("No WebGPU thread...".to_owned())), - }; + let script_to_constellation_chan = global.script_to_constellation_chan(); + if script_to_constellation_chan + .send(ScriptMsg::RequestAdapter( + sender, + wgpu::instance::RequestAdapterOptions { power_preference }, + ids, + )) + .is_err() + { + promise.reject_error(Error::Operation); + } promise } } @@ -145,19 +141,17 @@ impl GPUMethods for GPU { impl AsyncWGPUListener for GPU { fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>) { match response { - WebGPUResponse::RequestAdapter(name, adapter) => { + WebGPUResponse::RequestAdapter(name, adapter, channel) => { let adapter = GPUAdapter::new( &self.global(), + channel, DOMString::from(format!("{} ({:?})", name, adapter.0.backend())), Heap::default(), adapter, ); promise.resolve_native(&adapter); }, - response => promise.reject_error(Error::Type(format!( - "Wrong response received for GPU from WebGPU thread {:?}", - response, - ))), + _ => promise.reject_error(Error::Operation), } } } diff --git a/components/script/dom/gpuadapter.rs b/components/script/dom/gpuadapter.rs index cbb24917ebe..ca656af5044 100644 --- a/components/script/dom/gpuadapter.rs +++ b/components/script/dom/gpuadapter.rs @@ -23,11 +23,13 @@ use dom_struct::dom_struct; use js::jsapi::{Heap, JSObject}; use std::ptr::NonNull; use std::rc::Rc; -use webgpu::{wgpu, WebGPUAdapter, WebGPURequest, WebGPUResponse}; +use webgpu::{wgpu, WebGPU, WebGPUAdapter, WebGPURequest, WebGPUResponse}; #[dom_struct] pub struct GPUAdapter { reflector_: Reflector, + #[ignore_malloc_size_of = "channels are hard"] + channel: WebGPU, name: DOMString, #[ignore_malloc_size_of = "mozjs"] extensions: Heap<*mut JSObject>, @@ -36,12 +38,14 @@ pub struct GPUAdapter { impl GPUAdapter { pub fn new_inherited( + channel: WebGPU, name: DOMString, extensions: Heap<*mut JSObject>, adapter: WebGPUAdapter, ) -> GPUAdapter { GPUAdapter { reflector_: Reflector::new(), + channel, name, extensions, adapter, @@ -50,12 +54,15 @@ impl GPUAdapter { pub fn new( global: &GlobalScope, + channel: WebGPU, name: DOMString, extensions: Heap<*mut JSObject>, adapter: WebGPUAdapter, ) -> DomRoot<GPUAdapter> { reflect_dom_object( - Box::new(GPUAdapter::new_inherited(name, extensions, adapter)), + Box::new(GPUAdapter::new_inherited( + channel, name, extensions, adapter, + )), global, GPUAdapterBinding::Wrap, ) @@ -89,15 +96,16 @@ impl GPUAdapterMethods for GPUAdapter { let id = window .Navigator() .create_device_id(self.adapter.0.backend()); - match window.webgpu_channel() { - Some(thread) => thread - .0 - .send(WebGPURequest::RequestDevice(sender, self.adapter, desc, id)) - .unwrap(), - None => promise.reject_error(Error::Type("No WebGPU thread...".to_owned())), + if self + .channel + .0 + .send(WebGPURequest::RequestDevice(sender, self.adapter, desc, id)) + .is_err() + { + promise.reject_error(Error::Operation); } } else { - promise.reject_error(Error::Type("No WebGPU thread...".to_owned())) + promise.reject_error(Error::Operation); }; promise } @@ -109,6 +117,7 @@ impl AsyncWGPUListener for GPUAdapter { WebGPUResponse::RequestDevice(device_id, _descriptor) => { let device = GPUDevice::new( &self.global(), + self.channel.clone(), &self, Heap::default(), Heap::default(), @@ -116,9 +125,7 @@ impl AsyncWGPUListener for GPUAdapter { ); promise.resolve_native(&device); }, - _ => promise.reject_error(Error::Type( - "Wrong response type from WebGPU thread...".to_owned(), - )), + _ => promise.reject_error(Error::Operation), } } } diff --git a/components/script/dom/gpubindgrouplayout.rs b/components/script/dom/gpubindgrouplayout.rs new file mode 100644 index 00000000000..7669f0e5f2d --- /dev/null +++ b/components/script/dom/gpubindgrouplayout.rs @@ -0,0 +1,75 @@ +/* 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::GPUBindGroupLayoutBinding::{ + self, GPUBindGroupLayoutBindings, GPUBindGroupLayoutMethods, +}; +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::{WebGPU, WebGPUBindGroupLayout}; + +#[dom_struct] +pub struct GPUBindGroupLayout { + reflector_: Reflector, + label: DomRefCell<Option<DOMString>>, + bind_group_layout: WebGPUBindGroupLayout, + #[ignore_malloc_size_of = "defined in webgpu"] + bindings: Vec<GPUBindGroupLayoutBindings>, + #[ignore_malloc_size_of = "defined in webgpu"] + channel: WebGPU, + valid: Cell<bool>, +} + +impl GPUBindGroupLayout { + fn new_inherited( + channel: WebGPU, + bind_group_layout: WebGPUBindGroupLayout, + bindings: Vec<GPUBindGroupLayoutBindings>, + valid: bool, + ) -> GPUBindGroupLayout { + Self { + reflector_: Reflector::new(), + channel, + label: DomRefCell::new(None), + bind_group_layout, + bindings, + valid: Cell::new(valid), + } + } + pub fn new( + global: &GlobalScope, + channel: WebGPU, + bind_group_layout: WebGPUBindGroupLayout, + bindings: Vec<GPUBindGroupLayoutBindings>, + valid: bool, + ) -> DomRoot<GPUBindGroupLayout> { + reflect_dom_object( + Box::new(GPUBindGroupLayout::new_inherited( + channel, + bind_group_layout, + bindings, + valid, + )), + global, + GPUBindGroupLayoutBinding::Wrap, + ) + } +} + +impl GPUBindGroupLayoutMethods for GPUBindGroupLayout { + /// 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 a2a3e3b62eb..160ef6cf79c 100644 --- a/components/script/dom/gpubuffer.rs +++ b/components/script/dom/gpubuffer.rs @@ -11,9 +11,8 @@ use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; use dom_struct::dom_struct; -use ipc_channel::ipc::IpcSender; use std::cell::Cell; -use webgpu::{WebGPUBuffer, WebGPUDevice, WebGPURequest}; +use webgpu::{WebGPU, WebGPUBuffer, WebGPUDevice, WebGPURequest}; #[derive(MallocSizeOf)] pub enum GPUBufferState { @@ -26,7 +25,7 @@ pub enum GPUBufferState { pub struct GPUBuffer { reflector_: Reflector, #[ignore_malloc_size_of = "channels are hard"] - channel: IpcSender<WebGPURequest>, + channel: WebGPU, label: DomRefCell<Option<DOMString>>, size: GPUBufferSize, usage: u32, @@ -38,7 +37,7 @@ pub struct GPUBuffer { impl GPUBuffer { fn new_inherited( - channel: IpcSender<WebGPURequest>, + channel: WebGPU, buffer: WebGPUBuffer, device: WebGPUDevice, state: GPUBufferState, @@ -62,7 +61,7 @@ impl GPUBuffer { #[allow(unsafe_code)] pub fn new( global: &GlobalScope, - channel: IpcSender<WebGPURequest>, + channel: WebGPU, buffer: WebGPUBuffer, device: WebGPUDevice, state: GPUBufferState, @@ -90,6 +89,7 @@ impl GPUBufferMethods for GPUBuffer { /// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-unmap fn Unmap(&self) { self.channel + .0 .send(WebGPURequest::UnmapBuffer(self.buffer)) .unwrap(); } @@ -103,6 +103,7 @@ impl GPUBufferMethods for GPUBuffer { _ => {}, }; self.channel + .0 .send(WebGPURequest::DestroyBuffer(self.buffer)) .unwrap(); *self.state.borrow_mut() = GPUBufferState::Destroyed; diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs index 81974735b55..d2d2b2013d8 100644 --- a/components/script/dom/gpudevice.rs +++ b/components/script/dom/gpudevice.rs @@ -5,6 +5,10 @@ #![allow(unsafe_code)] use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::GPUAdapterBinding::GPULimits; +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::WindowBinding::WindowBinding::WindowMethods; @@ -15,6 +19,7 @@ use crate::dom::bindings::str::DOMString; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::gpuadapter::GPUAdapter; +use crate::dom::gpubindgrouplayout::GPUBindGroupLayout; use crate::dom::gpubuffer::{GPUBuffer, GPUBufferState}; use crate::dom::window::Window; use crate::script_runtime::JSContext as SafeJSContext; @@ -23,13 +28,17 @@ use ipc_channel::ipc; use js::jsapi::{Heap, JSObject}; use js::jsval::{JSVal, ObjectValue, UndefinedValue}; 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::resource::{BufferDescriptor, BufferUsage}; -use webgpu::{WebGPUBuffer, WebGPUDevice, WebGPURequest}; +use webgpu::{WebGPU, WebGPUBuffer, WebGPUDevice, WebGPURequest}; #[dom_struct] pub struct GPUDevice { eventtarget: EventTarget, + #[ignore_malloc_size_of = "channels are hard"] + channel: WebGPU, adapter: Dom<GPUAdapter>, #[ignore_malloc_size_of = "mozjs"] extensions: Heap<*mut JSObject>, @@ -41,6 +50,7 @@ pub struct GPUDevice { impl GPUDevice { fn new_inherited( + channel: WebGPU, adapter: &GPUAdapter, extensions: Heap<*mut JSObject>, limits: Heap<*mut JSObject>, @@ -48,6 +58,7 @@ impl GPUDevice { ) -> GPUDevice { Self { eventtarget: EventTarget::new_inherited(), + channel, adapter: Dom::from_ref(adapter), extensions, limits, @@ -59,6 +70,7 @@ impl GPUDevice { #[allow(unsafe_code)] pub fn new( global: &GlobalScope, + channel: WebGPU, adapter: &GPUAdapter, extensions: Heap<*mut JSObject>, limits: Heap<*mut JSObject>, @@ -66,7 +78,7 @@ impl GPUDevice { ) -> DomRoot<GPUDevice> { reflect_dom_object( Box::new(GPUDevice::new_inherited( - adapter, extensions, limits, device, + channel, adapter, extensions, limits, device, )), global, GPUDeviceBinding::Wrap, @@ -78,7 +90,6 @@ impl GPUDevice { unsafe fn resolve_create_buffer_mapped( &self, cx: SafeJSContext, - channel: ipc_channel::ipc::IpcSender<WebGPURequest>, gpu_buffer: WebGPUBuffer, array_buffer: Vec<u8>, descriptor: BufferDescriptor, @@ -95,7 +106,7 @@ impl GPUDevice { let buff = GPUBuffer::new( &self.global(), - channel, + self.channel.clone(), gpu_buffer, self.device, GPUBufferState::Mapped, @@ -165,25 +176,18 @@ impl GPUDeviceMethods for GPUDevice { /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbuffer fn CreateBuffer(&self, descriptor: &GPUBufferDescriptor) -> DomRoot<GPUBuffer> { let (valid, wgpu_descriptor) = self.validate_buffer_descriptor(descriptor); - let channel; let (sender, receiver) = ipc::channel().unwrap(); if let Some(window) = self.global().downcast::<Window>() { let id = window.Navigator().create_buffer_id(self.device.0.backend()); - match window.webgpu_channel() { - Some(thread) => { - channel = thread.0.clone(); - thread - .0 - .send(WebGPURequest::CreateBuffer( - sender, - self.device, - id, - wgpu_descriptor, - )) - .unwrap(); - }, - None => unimplemented!(), - } + self.channel + .0 + .send(WebGPURequest::CreateBuffer( + sender, + self.device, + id, + wgpu_descriptor, + )) + .expect("Failed to create WebGPU buffer"); } else { unimplemented!() }; @@ -192,7 +196,7 @@ impl GPUDeviceMethods for GPUDevice { GPUBuffer::new( &self.global(), - channel, + self.channel.clone(), buffer, self.device, GPUBufferState::Unmapped, @@ -209,26 +213,19 @@ impl GPUDeviceMethods for GPUDevice { descriptor: &GPUBufferDescriptor, ) -> Vec<JSVal> { let (valid, wgpu_descriptor) = self.validate_buffer_descriptor(descriptor); - let channel; let (sender, receiver) = ipc::channel().unwrap(); rooted!(in(*cx) let js_val = UndefinedValue()); if let Some(window) = self.global().downcast::<Window>() { let id = window.Navigator().create_buffer_id(self.device.0.backend()); - match window.webgpu_channel() { - Some(thread) => { - channel = thread.0.clone(); - thread - .0 - .send(WebGPURequest::CreateBufferMapped( - sender, - self.device, - id, - wgpu_descriptor.clone(), - )) - .unwrap() - }, - None => return vec![js_val.get()], - } + self.channel + .0 + .send(WebGPURequest::CreateBufferMapped( + sender, + self.device, + id, + wgpu_descriptor.clone(), + )) + .expect("Failed to create WebGPU buffer"); } else { return vec![js_val.get()]; }; @@ -236,14 +233,180 @@ impl GPUDeviceMethods for GPUDevice { let (buffer, array_buffer) = receiver.recv().unwrap(); unsafe { - self.resolve_create_buffer_mapped( - cx, - channel, - buffer, - array_buffer, - wgpu_descriptor, - valid, - ) + self.resolve_create_buffer_mapped(cx, buffer, array_buffer, wgpu_descriptor, valid) + } + } + + /// https://gpuweb.github.io/gpuweb/#GPUDevice-createBindGroupLayout + fn CreateBindGroupLayout( + &self, + descriptor: &GPUBindGroupLayoutDescriptor, + ) -> DomRoot<GPUBindGroupLayout> { + #[derive(Clone)] + struct MaxLimits { + max_uniform_buffers_per_shader_stage: i32, + max_storage_buffers_per_shader_stage: i32, + max_sampled_textures_per_shader_stage: i32, + max_storage_textures_per_shader_stage: i32, + max_samplers_per_shader_stage: i32, + } + let mut storeBindings = HashSet::new(); + // TODO: We should have these limits on device creation + let limits = GPULimits::empty(); + + let mut validation_map = HashMap::new(); + let maxLimits = MaxLimits { + max_uniform_buffers_per_shader_stage: limits.maxUniformBuffersPerShaderStage as i32, + max_storage_buffers_per_shader_stage: limits.maxStorageBuffersPerShaderStage as i32, + max_sampled_textures_per_shader_stage: limits.maxSampledTexturesPerShaderStage as i32, + max_storage_textures_per_shader_stage: limits.maxStorageTexturesPerShaderStage as i32, + max_samplers_per_shader_stage: limits.maxSamplersPerShaderStage as i32, + }; + validation_map.insert( + webgpu::wgpu::binding_model::ShaderStage::VERTEX, + maxLimits.clone(), + ); + validation_map.insert( + webgpu::wgpu::binding_model::ShaderStage::FRAGMENT, + maxLimits.clone(), + ); + validation_map.insert( + webgpu::wgpu::binding_model::ShaderStage::COMPUTE, + maxLimits.clone(), + ); + let mut max_dynamic_uniform_buffers_per_pipeline_layout = + limits.maxDynamicUniformBuffersPerPipelineLayout as i32; + let mut max_dynamic_storage_buffers_per_pipeline_layout = + limits.maxDynamicStorageBuffersPerPipelineLayout as i32; + let mut valid = true; + + let bindings = descriptor + .bindings + .iter() + .map(|bind| { + // TODO: binding must be >= 0 + storeBindings.insert(bind.binding); + let visibility = match ShaderStage::from_bits(bind.visibility) { + Some(visibility) => visibility, + None => { + valid = false; + ShaderStage::from_bits(0).unwrap() + }, + }; + let ty = match bind.type_ { + GPUBindingType::Uniform_buffer => { + if let Some(limit) = validation_map.get_mut(&visibility) { + limit.max_uniform_buffers_per_shader_stage -= 1; + } + if bind.hasDynamicOffset { + max_dynamic_uniform_buffers_per_pipeline_layout -= 1; + }; + BindingType::UniformBuffer + }, + GPUBindingType::Storage_buffer => { + if let Some(limit) = validation_map.get_mut(&visibility) { + limit.max_storage_buffers_per_shader_stage -= 1; + } + if bind.hasDynamicOffset { + max_dynamic_storage_buffers_per_pipeline_layout -= 1; + }; + BindingType::StorageBuffer + }, + GPUBindingType::Readonly_storage_buffer => { + if let Some(limit) = validation_map.get_mut(&visibility) { + limit.max_storage_buffers_per_shader_stage -= 1; + } + if bind.hasDynamicOffset { + max_dynamic_storage_buffers_per_pipeline_layout -= 1; + }; + BindingType::ReadonlyStorageBuffer + }, + GPUBindingType::Sampled_texture => { + if let Some(limit) = validation_map.get_mut(&visibility) { + limit.max_sampled_textures_per_shader_stage -= 1; + } + if bind.hasDynamicOffset { + valid = false + }; + BindingType::SampledTexture + }, + GPUBindingType::Storage_texture => { + if let Some(limit) = validation_map.get_mut(&visibility) { + limit.max_storage_textures_per_shader_stage -= 1; + } + if bind.hasDynamicOffset { + valid = false + }; + BindingType::StorageTexture + }, + GPUBindingType::Sampler => { + if let Some(limit) = validation_map.get_mut(&visibility) { + limit.max_samplers_per_shader_stage -= 1; + } + if bind.hasDynamicOffset { + valid = false + }; + BindingType::Sampler + }, + }; + + BindGroupLayoutBinding { + binding: bind.binding, + visibility, + ty, + dynamic: bind.hasDynamicOffset, + multisampled: bind.multisampled, + texture_dimension: webgpu::wgpu::resource::TextureViewDimension::D2, // Use as default for now + } + }) + .collect::<Vec<BindGroupLayoutBinding>>(); + + // bindings are unique + valid &= storeBindings.len() == bindings.len(); + + // Ensure that values do not exceed the max limit for each ShaderStage. + valid &= validation_map.values().all(|stage| { + stage.max_uniform_buffers_per_shader_stage >= 0 && + stage.max_storage_buffers_per_shader_stage >= 0 && + stage.max_sampled_textures_per_shader_stage >= 0 && + stage.max_storage_textures_per_shader_stage >= 0 && + stage.max_samplers_per_shader_stage >= 0 + }); + + // DynamicValues does not exceed the max limit for the pipeline + valid &= max_dynamic_uniform_buffers_per_pipeline_layout >= 0 && + max_dynamic_storage_buffers_per_pipeline_layout >= 0; + + let (sender, receiver) = ipc::channel().unwrap(); + if let Some(window) = self.global().downcast::<Window>() { + let id = window + .Navigator() + .create_bind_group_layout_id(self.device.0.backend()); + self.channel + .0 + .send(WebGPURequest::CreateBindGroupLayout( + sender, + self.device, + id, + bindings.clone(), + )) + .expect("Failed to create WebGPU BindGroupLayout"); } + let bgl = receiver.recv().unwrap(); + + let binds = descriptor + .bindings + .iter() + .map(|bind| GPUBindGroupLayoutBindings { + binding: bind.binding, + hasDynamicOffset: bind.hasDynamicOffset, + multisampled: bind.multisampled, + type_: bind.type_, + visibility: bind.visibility, + //texture_dimension: bind.texture_dimension + }) + .collect::<Vec<_>>(); + + GPUBindGroupLayout::new(&self.global(), self.channel.clone(), bgl, binds, valid) } } diff --git a/components/script/dom/gpushaderstage.rs b/components/script/dom/gpushaderstage.rs new file mode 100644 index 00000000000..a9cd1d90589 --- /dev/null +++ b/components/script/dom/gpushaderstage.rs @@ -0,0 +1,11 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::reflector::Reflector; +use dom_struct::dom_struct; + +#[dom_struct] +pub struct GPUShaderStage { + reflector_: Reflector, +} diff --git a/components/script/dom/htmlformcontrolscollection.rs b/components/script/dom/htmlformcontrolscollection.rs index 05ea6426b27..8bf296a8417 100644 --- a/components/script/dom/htmlformcontrolscollection.rs +++ b/components/script/dom/htmlformcontrolscollection.rs @@ -6,16 +6,17 @@ use crate::dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollecti use crate::dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding; use crate::dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding::HTMLFormControlsCollectionMethods; 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::str::DOMString; use crate::dom::element::Element; use crate::dom::htmlcollection::{CollectionFilter, HTMLCollection}; +use crate::dom::htmlformelement::HTMLFormElement; use crate::dom::node::Node; use crate::dom::radionodelist::RadioNodeList; use crate::dom::window::Window; use dom_struct::dom_struct; -use std::iter; #[dom_struct] pub struct HTMLFormControlsCollection { @@ -24,17 +25,17 @@ pub struct HTMLFormControlsCollection { impl HTMLFormControlsCollection { fn new_inherited( - root: &Node, + root: &HTMLFormElement, filter: Box<dyn CollectionFilter + 'static>, ) -> HTMLFormControlsCollection { HTMLFormControlsCollection { - collection: HTMLCollection::new_inherited(root, filter), + collection: HTMLCollection::new_inherited(root.upcast::<Node>(), filter), } } pub fn new( window: &Window, - root: &Node, + root: &HTMLFormElement, filter: Box<dyn CollectionFilter + 'static>, ) -> DomRoot<HTMLFormControlsCollection> { reflect_dom_object( @@ -76,12 +77,16 @@ impl HTMLFormControlsCollectionMethods for HTMLFormControlsCollection { Some(RadioNodeListOrElement::Element(elem)) } else { // Step 4-5 - let once = iter::once(DomRoot::upcast::<Node>(elem)); - let list = once.chain(peekable.map(DomRoot::upcast)); 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_simple_list(window, list), + RadioNodeList::new_controls_except_image_inputs(window, form, name), )) } // Step 3 diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index b788c113b8e..c36f30c1fb3 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -13,6 +13,7 @@ use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding; use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMethods; use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods; use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods; +use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId}; use crate::dom::bindings::refcounted::Trusted; @@ -45,6 +46,8 @@ use crate::dom::htmltextareaelement::HTMLTextAreaElement; use crate::dom::node::{document_from_node, window_from_node}; 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::validitystate::ValidationFlags; use crate::dom::virtualmethods::VirtualMethods; use crate::dom::window::Window; @@ -65,7 +68,6 @@ use style::attr::AttrValue; use style::str::split_html_space_chars; use crate::dom::bindings::codegen::UnionTypes::RadioNodeListOrElement; -use crate::dom::radionodelist::RadioNodeList; use std::collections::HashMap; use time::{now, Duration, Tm}; @@ -115,6 +117,69 @@ impl HTMLFormElement { HTMLFormElementBinding::Wrap, ) } + + fn filter_for_radio_list(mode: RadioListMode, child: &Element, name: &DOMString) -> bool { + if let Some(child) = child.downcast::<Element>() { + match mode { + RadioListMode::ControlsExceptImageInputs => { + if child + .downcast::<HTMLElement>() + .map_or(false, |c| c.is_listed_element()) + { + if (child.has_attribute(&local_name!("id")) && + child.get_string_attribute(&local_name!("id")) == *name) || + (child.has_attribute(&local_name!("name")) && + child.get_string_attribute(&local_name!("name")) == *name) + { + if let Some(inp) = child.downcast::<HTMLInputElement>() { + // input, only return it if it's not image-button state + return inp.input_type() != InputType::Image; + } else { + // control, but not an input + return true; + } + } + } + return false; + }, + RadioListMode::Images => { + if child.is::<HTMLImageElement>() { + if (child.has_attribute(&local_name!("id")) && + child.get_string_attribute(&local_name!("id")) == *name) || + (child.has_attribute(&local_name!("name")) && + child.get_string_attribute(&local_name!("name")) == *name) + { + return true; + } + } + return false; + }, + } + } + false + } + + pub fn nth_for_radio_list( + &self, + index: u32, + mode: RadioListMode, + name: &DOMString, + ) -> Option<DomRoot<Node>> { + self.controls + .borrow() + .iter() + .filter(|n| HTMLFormElement::filter_for_radio_list(mode, &*n, name)) + .nth(index as usize) + .and_then(|n| Some(DomRoot::from_ref(n.upcast::<Node>()))) + } + + pub fn count_for_radio_list(&self, mode: RadioListMode, name: &DOMString) -> u32 { + self.controls + .borrow() + .iter() + .filter(|n| HTMLFormElement::filter_for_radio_list(mode, &**n, name)) + .count() as u32 + } } impl HTMLFormElementMethods for HTMLFormElement { @@ -248,7 +313,7 @@ impl HTMLFormElementMethods for HTMLFormElement { form: DomRoot::from_ref(self), }); let window = window_from_node(self); - HTMLFormControlsCollection::new(&window, self.upcast(), filter) + HTMLFormControlsCollection::new(&window, self, filter) })) } @@ -265,43 +330,23 @@ impl HTMLFormElementMethods for HTMLFormElement { // https://html.spec.whatwg.org/multipage/#the-form-element%3Adetermine-the-value-of-a-named-property fn NamedGetter(&self, name: DOMString) -> Option<RadioNodeListOrElement> { - let mut candidates: Vec<DomRoot<Node>> = Vec::new(); + let window = window_from_node(self); - let controls = self.controls.borrow(); // Step 1 - for child in controls.iter() { - if child - .downcast::<HTMLElement>() - .map_or(false, |c| c.is_listed_element()) - { - if (child.has_attribute(&local_name!("id")) && - child.get_string_attribute(&local_name!("id")) == name) || - (child.has_attribute(&local_name!("name")) && - child.get_string_attribute(&local_name!("name")) == name) - { - candidates.push(DomRoot::from_ref(&*child.upcast::<Node>())); - } - } - } + let mut candidates = + RadioNodeList::new_controls_except_image_inputs(&window, self, name.clone()); + let mut candidates_length = candidates.Length(); + // Step 2 - if candidates.len() == 0 { - for child in controls.iter() { - if child.is::<HTMLImageElement>() { - if (child.has_attribute(&local_name!("id")) && - child.get_string_attribute(&local_name!("id")) == name) || - (child.has_attribute(&local_name!("name")) && - child.get_string_attribute(&local_name!("name")) == name) - { - candidates.push(DomRoot::from_ref(&*child.upcast::<Node>())); - } - } - } + if candidates_length == 0 { + candidates = RadioNodeList::new_images(&window, self, name.clone()); + candidates_length = candidates.Length(); } let mut past_names_map = self.past_names_map.borrow_mut(); // Step 3 - if candidates.len() == 0 { + if candidates_length == 0 { if past_names_map.contains_key(&name) { return Some(RadioNodeListOrElement::Element(DomRoot::from_ref( &*past_names_map.get(&name).unwrap().0, @@ -311,16 +356,13 @@ impl HTMLFormElementMethods for HTMLFormElement { } // Step 4 - if candidates.len() > 1 { - let window = window_from_node(self); - - return Some(RadioNodeListOrElement::RadioNodeList( - RadioNodeList::new_simple_list(&window, candidates.into_iter()), - )); + if candidates_length > 1 { + return Some(RadioNodeListOrElement::RadioNodeList(candidates)); } // Step 5 - let element_node = &candidates[0]; + // candidates_length is 1, so we can unwrap item 0 + let element_node = candidates.upcast::<NodeList>().Item(0).unwrap(); past_names_map.insert( name, ( diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 90058f303a9..22333a17051 100755 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -5,12 +5,14 @@ use crate::dom::activation::{synthetic_click_activation, Activatable, ActivationSource}; use crate::dom::attr::Attr; use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods; use crate::dom::bindings::codegen::Bindings::FileListBinding::FileListMethods; use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::SelectionMode; use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding; use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods; use crate::dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods; +use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods}; use crate::dom::bindings::error::{Error, ErrorResult}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::DomObject; @@ -26,6 +28,7 @@ use crate::dom::eventtarget::EventTarget; use crate::dom::file::File; use crate::dom::filelist::FileList; use crate::dom::globalscope::GlobalScope; +use crate::dom::htmldatalistelement::HTMLDataListElement; use crate::dom::htmlelement::HTMLElement; use crate::dom::htmlfieldsetelement::HTMLFieldSetElement; use crate::dom::htmlformelement::{ @@ -35,21 +38,29 @@ use crate::dom::htmlformelement::{ResetFrom, SubmittedFrom}; use crate::dom::keyboardevent::KeyboardEvent; use crate::dom::mouseevent::MouseEvent; use crate::dom::node::{document_from_node, window_from_node}; -use crate::dom::node::{BindContext, CloneChildrenFlag, Node, NodeDamage, UnbindContext}; +use crate::dom::node::{ + BindContext, CloneChildrenFlag, Node, NodeDamage, ShadowIncluding, UnbindContext, +}; use crate::dom::nodelist::NodeList; use crate::dom::textcontrol::{TextControlElement, TextControlSelection}; use crate::dom::validation::Validatable; use crate::dom::validitystate::ValidationFlags; use crate::dom::virtualmethods::VirtualMethods; +use crate::script_runtime::JSContext as SafeJSContext; use crate::textinput::KeyReaction::{ DispatchInput, Nothing, RedrawSelection, TriggerDefaultAction, }; use crate::textinput::Lines::Single; use crate::textinput::{Direction, SelectionDirection, TextInput, UTF16CodeUnits, UTF8Bytes}; +use chrono::naive::{NaiveDate, NaiveDateTime}; +use chrono::{Datelike, Weekday}; use dom_struct::dom_struct; use embedder_traits::FilterPattern; use encoding_rs::Encoding; use html5ever::{LocalName, Prefix}; +use js::jsapi::{ + ClippedTime, DateGetMsecSinceEpoch, Handle, JSObject, NewDateObject, ObjectIsDate, +}; use msg::constellation_msg::InputMethodType; use net_traits::blob_url_store::get_blob_origin; use net_traits::filemanager_thread::FileManagerThreadMsg; @@ -61,6 +72,7 @@ use servo_atoms::Atom; use std::borrow::ToOwned; use std::cell::Cell; use std::ops::Range; +use std::ptr::NonNull; use style::attr::AttrValue; use style::element_state::ElementState; use style::str::{split_commas, str_join}; @@ -217,6 +229,12 @@ enum ValueMode { Filename, } +#[derive(Debug, PartialEq)] +enum StepDirection { + Up, + Down, +} + #[dom_struct] pub struct HTMLInputElement { htmlelement: HTMLElement, @@ -231,6 +249,10 @@ pub struct HTMLInputElement { activation_state: DomRefCell<InputActivationState>, // https://html.spec.whatwg.org/multipage/#concept-input-value-dirty-flag value_dirty: Cell<bool>, + // not specified explicitly, but implied by the fact that sanitization can't + // happen until after all of step/min/max/value content attributes have + // been added + sanitization_flag: Cell<bool>, filelist: MutNullableDom<FileList>, form_owner: MutNullableDom<HTMLFormElement>, @@ -302,6 +324,7 @@ impl HTMLInputElement { )), activation_state: DomRefCell::new(InputActivationState::new()), value_dirty: Cell::new(false), + sanitization_flag: Cell::new(true), filelist: MutNullableDom::new(None), form_owner: Default::default(), labels_node_list: MutNullableDom::new(None), @@ -358,6 +381,318 @@ impl HTMLInputElement { pub fn input_type(&self) -> InputType { self.input_type.get() } + + pub fn disable_sanitization(&self) { + self.sanitization_flag.set(false); + } + + pub fn enable_sanitization(&self) { + self.sanitization_flag.set(true); + let mut textinput = self.textinput.borrow_mut(); + let mut value = textinput.single_line_content().clone(); + self.sanitize_value(&mut value); + textinput.set_content(value); + } + + // valueAsNumber, step, min, and max all share the same set of + // input types they apply to + fn does_value_as_number_apply(&self) -> bool { + match self.input_type() { + InputType::Date | + InputType::Month | + InputType::Week | + InputType::Time | + InputType::DatetimeLocal | + InputType::Number | + InputType::Range => true, + _ => false, + } + } + + fn does_value_as_date_apply(&self) -> bool { + match self.input_type() { + InputType::Date | InputType::Month | InputType::Week | InputType::Time => true, + // surprisingly, spec says false for DateTimeLocal! + _ => false, + } + } + + // https://html.spec.whatwg.org/multipage#concept-input-step + fn allowed_value_step(&self) -> Option<f64> { + if let Some(attr) = self + .upcast::<Element>() + .get_attribute(&ns!(), &local_name!("step")) + { + if let Ok(step) = DOMString::from(attr.summarize().value).parse_floating_point_number() + { + if step > 0.0 { + return Some(step * self.step_scale_factor()); + } + } + } + self.default_step() + .map(|step| step * self.step_scale_factor()) + } + + // https://html.spec.whatwg.org/multipage#concept-input-min + fn minimum(&self) -> Option<f64> { + if let Some(attr) = self + .upcast::<Element>() + .get_attribute(&ns!(), &local_name!("min")) + { + if let Ok(min) = self.convert_string_to_number(&DOMString::from(attr.summarize().value)) + { + return Some(min); + } + } + return self.default_minimum(); + } + + // https://html.spec.whatwg.org/multipage#concept-input-max + fn maximum(&self) -> Option<f64> { + if let Some(attr) = self + .upcast::<Element>() + .get_attribute(&ns!(), &local_name!("max")) + { + if let Ok(max) = self.convert_string_to_number(&DOMString::from(attr.summarize().value)) + { + return Some(max); + } + } + return self.default_maximum(); + } + + // when allowed_value_step and minumum both exist, this is the smallest + // value >= minimum that lies on an integer step + fn stepped_minimum(&self) -> Option<f64> { + match (self.minimum(), self.allowed_value_step()) { + (Some(min), Some(allowed_step)) => { + let step_base = self.step_base(); + // how many steps is min from step_base? + let nsteps = (min - step_base) / allowed_step; + // count that many integer steps, rounded +, from step_base + Some(step_base + (allowed_step * nsteps.ceil())) + }, + (_, _) => None, + } + } + + // when allowed_value_step and maximum both exist, this is the smallest + // value <= maximum that lies on an integer step + fn stepped_maximum(&self) -> Option<f64> { + match (self.maximum(), self.allowed_value_step()) { + (Some(max), Some(allowed_step)) => { + let step_base = self.step_base(); + // how many steps is max from step_base? + let nsteps = (max - step_base) / allowed_step; + // count that many integer steps, rounded -, from step_base + Some(step_base + (allowed_step * nsteps.floor())) + }, + (_, _) => None, + } + } + + // https://html.spec.whatwg.org/multipage#concept-input-min-default + fn default_minimum(&self) -> Option<f64> { + match self.input_type() { + InputType::Range => Some(0.0), + _ => None, + } + } + + // https://html.spec.whatwg.org/multipage#concept-input-max-default + fn default_maximum(&self) -> Option<f64> { + match self.input_type() { + InputType::Range => Some(100.0), + _ => None, + } + } + + // https://html.spec.whatwg.org/multipage#concept-input-value-default-range + fn default_range_value(&self) -> f64 { + let min = self.minimum().unwrap_or(0.0); + let max = self.maximum().unwrap_or(100.0); + if max < min { + min + } else { + min + (max - min) * 0.5 + } + } + + // https://html.spec.whatwg.org/multipage#concept-input-step-default + fn default_step(&self) -> Option<f64> { + match self.input_type() { + InputType::Date => Some(1.0), + InputType::Month => Some(1.0), + InputType::Week => Some(1.0), + InputType::Time => Some(60.0), + InputType::DatetimeLocal => Some(60.0), + InputType::Number => Some(1.0), + InputType::Range => Some(1.0), + _ => None, + } + } + + // https://html.spec.whatwg.org/multipage#concept-input-step-scale + fn step_scale_factor(&self) -> f64 { + match self.input_type() { + InputType::Date => 86400000.0, + InputType::Month => 1.0, + InputType::Week => 604800000.0, + InputType::Time => 1000.0, + InputType::DatetimeLocal => 1000.0, + InputType::Number => 1.0, + InputType::Range => 1.0, + _ => unreachable!(), + } + } + + // https://html.spec.whatwg.org/multipage#concept-input-min-zero + fn step_base(&self) -> f64 { + if let Some(attr) = self + .upcast::<Element>() + .get_attribute(&ns!(), &local_name!("min")) + { + let minstr = &DOMString::from(attr.summarize().value); + if let Ok(min) = self.convert_string_to_number(minstr) { + return min; + } + } + if let Some(attr) = self + .upcast::<Element>() + .get_attribute(&ns!(), &local_name!("value")) + { + if let Ok(value) = + self.convert_string_to_number(&DOMString::from(attr.summarize().value)) + { + return value; + } + } + self.default_step_base().unwrap_or(0.0) + } + + // https://html.spec.whatwg.org/multipage#concept-input-step-default-base + fn default_step_base(&self) -> Option<f64> { + match self.input_type() { + InputType::Week => Some(-259200000.0), + _ => None, + } + } + + // https://html.spec.whatwg.org/multipage/#dom-input-stepdown + // https://html.spec.whatwg.org/multipage/#dom-input-stepup + fn step_up_or_down(&self, n: i32, dir: StepDirection) -> ErrorResult { + // Step 1 + if !self.does_value_as_number_apply() { + return Err(Error::InvalidState); + } + let step_base = self.step_base(); + // Step 2 + let allowed_value_step = match self.allowed_value_step() { + Some(avs) => avs, + None => return Err(Error::InvalidState), + }; + let minimum = self.minimum(); + let maximum = self.maximum(); + if let (Some(min), Some(max)) = (minimum, maximum) { + // Step 3 + if min > max { + return Ok(()); + } + // Step 4 + if let Some(smin) = self.stepped_minimum() { + if smin > max { + return Ok(()); + } + } + } + // Step 5 + let mut value: f64 = self.convert_string_to_number(&self.Value()).unwrap_or(0.0); + + // Step 6 + let valueBeforeStepping = value; + + // Step 7 + if (value - step_base) % allowed_value_step != 0.0 { + value = match dir { + StepDirection::Down => + //step down a fractional step to be on a step multiple + { + let intervals_from_base = ((value - step_base) / allowed_value_step).floor(); + intervals_from_base * allowed_value_step + step_base + } + StepDirection::Up => + // step up a fractional step to be on a step multiple + { + let intervals_from_base = ((value - step_base) / allowed_value_step).ceil(); + intervals_from_base * allowed_value_step + step_base + } + }; + } else { + value = value + + match dir { + StepDirection::Down => -f64::from(n) * allowed_value_step, + StepDirection::Up => f64::from(n) * allowed_value_step, + }; + } + + // Step 8 + if let Some(min) = minimum { + if value < min { + value = self.stepped_minimum().unwrap_or(value); + } + } + + // Step 9 + if let Some(max) = maximum { + if value > max { + value = self.stepped_maximum().unwrap_or(value); + } + } + + // Step 10 + match dir { + StepDirection::Down => { + if value > valueBeforeStepping { + return Ok(()); + } + }, + StepDirection::Up => { + if value < valueBeforeStepping { + return Ok(()); + } + }, + } + + // Step 11 + self.SetValueAsNumber(value) + } + + // https://html.spec.whatwg.org/multipage/#concept-input-list + fn suggestions_source_element(&self) -> Option<DomRoot<HTMLElement>> { + let list_string = self + .upcast::<Element>() + .get_string_attribute(&local_name!("list")); + if list_string.is_empty() { + return None; + } + let ancestor = self + .upcast::<Node>() + .GetRootNode(&GetRootNodeOptions::empty()); + let first_with_id = &ancestor + .traverse_preorder(ShadowIncluding::No) + .find(|node| { + node.downcast::<Element>() + .map_or(false, |e| e.Id() == list_string) + }); + first_with_id + .as_ref() + .and_then(|el| { + el.downcast::<HTMLDataListElement>() + .map(|data_el| data_el.upcast::<HTMLElement>()) + }) + .map(|el| DomRoot::from_ref(&*el)) + } } pub trait LayoutHTMLInputElementHelpers { @@ -678,6 +1013,96 @@ impl HTMLInputElementMethods for HTMLInputElement { // https://html.spec.whatwg.org/multipage/#dom-input-defaultvalue make_setter!(SetDefaultValue, "value"); + // https://html.spec.whatwg.org/multipage/#dom-input-min + make_getter!(Min, "min"); + + // https://html.spec.whatwg.org/multipage/#dom-input-min + make_setter!(SetMin, "min"); + + // https://html.spec.whatwg.org/multipage/#dom-input-list + fn GetList(&self) -> Option<DomRoot<HTMLElement>> { + self.suggestions_source_element() + } + + // https://html.spec.whatwg.org/multipage/#dom-input-valueasdate + #[allow(unsafe_code)] + fn GetValueAsDate(&self, cx: SafeJSContext) -> Option<NonNull<JSObject>> { + self.convert_string_to_naive_datetime(self.Value()) + .map(|dt| unsafe { + let time = ClippedTime { + t: dt.timestamp_millis() as f64, + }; + NonNull::new_unchecked(NewDateObject(*cx, time)) + }) + .ok() + } + + // https://html.spec.whatwg.org/multipage/#dom-input-valueasdate + #[allow(unsafe_code)] + fn SetValueAsDate(&self, cx: SafeJSContext, value: *mut JSObject) -> ErrorResult { + rooted!(in(*cx) let value = value); + if !self.does_value_as_date_apply() { + return Err(Error::InvalidState); + } + if value.is_null() { + return self.SetValue(DOMString::from("")); + } + let mut msecs: f64 = 0.0; + // We need to go through unsafe code to interrogate jsapi about a Date. + // To minimize the amount of unsafe code to maintain, this just gets the milliseconds, + // which we then reinflate into a NaiveDate for use in safe code. + unsafe { + let mut isDate = false; + if !ObjectIsDate(*cx, Handle::from(value.handle()), &mut isDate) { + return Err(Error::JSFailed); + } + if !isDate { + return Err(Error::Type("Value was not a date".to_string())); + } + if !DateGetMsecSinceEpoch(*cx, Handle::from(value.handle()), &mut msecs) { + return Err(Error::JSFailed); + } + if !msecs.is_finite() { + return self.SetValue(DOMString::from("")); + } + } + // now we make a Rust date out of it so we can use safe code for the + // actual conversion logic + match milliseconds_to_datetime(msecs) { + Ok(dt) => match self.convert_naive_datetime_to_string(dt) { + Ok(converted) => self.SetValue(converted), + _ => self.SetValue(DOMString::from("")), + }, + _ => self.SetValue(DOMString::from("")), + } + } + + // https://html.spec.whatwg.org/multipage/#dom-input-valueasnumber + fn ValueAsNumber(&self) -> f64 { + self.convert_string_to_number(&self.Value()) + .unwrap_or(std::f64::NAN) + } + + // https://html.spec.whatwg.org/multipage/#dom-input-valueasnumber + fn SetValueAsNumber(&self, value: f64) -> ErrorResult { + if value.is_infinite() { + Err(Error::Type("value is not finite".to_string())) + } else if !self.does_value_as_number_apply() { + Err(Error::InvalidState) + } else if value.is_nan() { + self.SetValue(DOMString::from("")) + } else if let Ok(converted) = self.convert_number_to_string(value) { + self.SetValue(converted) + } else { + // The most literal spec-compliant implementation would + // use bignum chrono types so overflow is impossible, + // but just setting an overflow to the empty string matches + // Firefox's behavior. + // (for example, try input.valueAsNumber=1e30 on a type="date" input) + self.SetValue(DOMString::from("")) + } + } + // https://html.spec.whatwg.org/multipage/#attr-fe-name make_getter!(Name, "name"); @@ -743,12 +1168,6 @@ impl HTMLInputElementMethods for HTMLInputElement { // https://html.spec.whatwg.org/multipage/#dom-input-minlength make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH); - // https://html.spec.whatwg.org/multipage/#dom-input-min - make_getter!(Min, "min"); - - // https://html.spec.whatwg.org/multipage/#dom-input-min - make_setter!(SetMin, "min"); - // https://html.spec.whatwg.org/multipage/#dom-input-multiple make_bool_getter!(Multiple, "multiple"); @@ -875,6 +1294,16 @@ impl HTMLInputElementMethods for HTMLInputElement { self.select_files(Some(paths)); } } + + // https://html.spec.whatwg.org/multipage/#dom-input-stepup + fn StepUp(&self, n: i32) -> ErrorResult { + self.step_up_or_down(n, StepDirection::Up) + } + + // https://html.spec.whatwg.org/multipage/#dom-input-stepdown + fn StepDown(&self, n: i32) -> ErrorResult { + self.step_up_or_down(n, StepDirection::Down) + } } #[allow(unsafe_code)] @@ -1160,6 +1589,13 @@ impl HTMLInputElement { // https://html.spec.whatwg.org/multipage/#value-sanitization-algorithm fn sanitize_value(&self, value: &mut DOMString) { + // if sanitization_flag is false, we are setting content attributes + // on an element we haven't really finished creating; we will + // enable the flag and really sanitize before this element becomes + // observable. + if !self.sanitization_flag.get() { + return; + } match self.input_type() { InputType::Text | InputType::Search | InputType::Tel | InputType::Password => { value.strip_newlines(); @@ -1216,10 +1652,63 @@ impl HTMLInputElement { if !value.is_valid_floating_point_number_string() { value.clear(); } + // Spec says that user agent "may" round the value + // when it's suffering a step mismatch, but WPT tests + // want it unrounded, and this matches other browser + // behavior (typing an unrounded number into an + // integer field box and pressing enter generally keeps + // the number intact but makes the input box :invalid) }, // https://html.spec.whatwg.org/multipage/#range-state-(type=range):value-sanitization-algorithm InputType::Range => { - value.set_best_representation_of_the_floating_point_number(); + if !value.is_valid_floating_point_number_string() { + *value = DOMString::from(self.default_range_value().to_string()); + } + if let Ok(fval) = &value.parse::<f64>() { + let mut fval = *fval; + // comparing max first, because if they contradict + // the spec wants min to be the one that applies + if let Some(max) = self.maximum() { + if fval > max { + fval = max; + } + } + if let Some(min) = self.minimum() { + if fval < min { + fval = min; + } + } + // https://html.spec.whatwg.org/multipage/#range-state-(type=range):suffering-from-a-step-mismatch + // Spec does not describe this in a way that lends itself to + // reproducible handling of floating-point rounding; + // Servo may fail a WPT test because .1 * 6 == 6.000000000000001 + if let Some(allowed_value_step) = self.allowed_value_step() { + let step_base = self.step_base(); + let steps_from_base = (fval - step_base) / allowed_value_step; + if steps_from_base.fract() != 0.0 { + // not an integer number of steps, there's a mismatch + // round the number of steps... + let int_steps = round_halves_positive(steps_from_base); + // and snap the value to that rounded value... + fval = int_steps * allowed_value_step + step_base; + + // but if after snapping we're now outside min..max + // we have to adjust! (adjusting to min last because + // that "wins" over max in the spec) + if let Some(stepped_maximum) = self.stepped_maximum() { + if fval > stepped_maximum { + fval = stepped_maximum; + } + } + if let Some(stepped_minimum) = self.stepped_minimum() { + if fval < stepped_minimum { + fval = stepped_minimum; + } + } + } + } + *value = DOMString::from(fval.to_string()); + }; }, InputType::Email => { if !self.Multiple() { @@ -1325,6 +1814,142 @@ impl HTMLInputElement { }, } } + + // https://html.spec.whatwg.org/multipage/#concept-input-value-string-number + fn convert_string_to_number(&self, value: &DOMString) -> Result<f64, ()> { + match self.input_type() { + InputType::Date => match value.parse_date_string() { + Ok((year, month, day)) => { + let d = NaiveDate::from_ymd(year, month, day); + let duration = d.signed_duration_since(NaiveDate::from_ymd(1970, 1, 1)); + Ok(duration.num_milliseconds() as f64) + }, + _ => Err(()), + }, + InputType::Month => match value.parse_month_string() { + // This one returns number of months, not milliseconds + // (specification requires this, presumably because number of + // milliseconds is not consistent across months) + // the - 1.0 is because january is 1, not 0 + Ok((year, month)) => Ok(((year - 1970) * 12) as f64 + (month as f64 - 1.0)), + _ => Err(()), + }, + InputType::Week => match value.parse_week_string() { + Ok((year, weeknum)) => { + let d = NaiveDate::from_isoywd(year, weeknum, Weekday::Mon); + let duration = d.signed_duration_since(NaiveDate::from_ymd(1970, 1, 1)); + Ok(duration.num_milliseconds() as f64) + }, + _ => Err(()), + }, + InputType::Time => match value.parse_time_string() { + Ok((hours, minutes, seconds)) => { + Ok((seconds as f64 + 60.0 * minutes as f64 + 3600.0 * hours as f64) * 1000.0) + }, + _ => Err(()), + }, + InputType::DatetimeLocal => match value.parse_local_date_and_time_string() { + // Is this supposed to know the locale's daylight-savings-time rules? + Ok(((year, month, day), (hours, minutes, seconds))) => { + let d = NaiveDate::from_ymd(year, month, day); + let ymd_duration = d.signed_duration_since(NaiveDate::from_ymd(1970, 1, 1)); + let hms_millis = + (seconds + 60.0 * minutes as f64 + 3600.0 * hours as f64) * 1000.0; + Ok(ymd_duration.num_milliseconds() as f64 + hms_millis) + }, + _ => Err(()), + }, + InputType::Number | InputType::Range => value.parse_floating_point_number(), + // min/max/valueAsNumber/stepDown/stepUp do not apply to + // the remaining types + _ => Err(()), + } + } + + // https://html.spec.whatwg.org/multipage/#concept-input-value-string-number + fn convert_number_to_string(&self, value: f64) -> Result<DOMString, ()> { + match self.input_type() { + InputType::Date => { + let datetime = milliseconds_to_datetime(value)?; + Ok(DOMString::from(datetime.format("%Y-%m-%d").to_string())) + }, + InputType::Month => { + // interpret value as months(not millis) in epoch, return monthstring + let year_from_1970 = (value / 12.0).floor(); + let month = (value - year_from_1970 * 12.0).floor() as u32 + 1; // january is 1, not 0 + let year = (year_from_1970 + 1970.0) as u64; + Ok(DOMString::from(format!("{:04}-{:02}", year, month))) + }, + InputType::Week => { + let datetime = milliseconds_to_datetime(value)?; + let year = datetime.iso_week().year(); // not necessarily the same as datetime.year() + let week = datetime.iso_week().week(); + Ok(DOMString::from(format!("{:04}-W{:02}", year, week))) + }, + InputType::Time => { + let datetime = milliseconds_to_datetime(value)?; + Ok(DOMString::from(datetime.format("%H:%M:%S%.3f").to_string())) + }, + InputType::DatetimeLocal => { + let datetime = milliseconds_to_datetime(value)?; + Ok(DOMString::from( + datetime.format("%Y-%m-%dT%H:%M:%S%.3f").to_string(), + )) + }, + InputType::Number | InputType::Range => Ok(DOMString::from(value.to_string())), + // this won't be called from other input types + _ => unreachable!(), + } + } + + // https://html.spec.whatwg.org/multipage/#concept-input-value-string-date + // This does the safe Rust part of conversion; the unsafe JS Date part + // is in GetValueAsDate + fn convert_string_to_naive_datetime(&self, value: DOMString) -> Result<NaiveDateTime, ()> { + match self.input_type() { + InputType::Date => value + .parse_date_string() + .and_then(|(y, m, d)| NaiveDate::from_ymd_opt(y, m, d).ok_or(())) + .map(|date| date.and_hms(0, 0, 0)), + InputType::Time => value.parse_time_string().and_then(|(h, m, s)| { + let whole_seconds = s.floor(); + let nanos = ((s - whole_seconds) * 1e9).floor() as u32; + NaiveDate::from_ymd(1970, 1, 1) + .and_hms_nano_opt(h, m, whole_seconds as u32, nanos) + .ok_or(()) + }), + InputType::Week => value + .parse_week_string() + .and_then(|(iso_year, week)| { + NaiveDate::from_isoywd_opt(iso_year, week, Weekday::Mon).ok_or(()) + }) + .map(|date| date.and_hms(0, 0, 0)), + InputType::Month => value + .parse_month_string() + .and_then(|(y, m)| NaiveDate::from_ymd_opt(y, m, 1).ok_or(())) + .map(|date| date.and_hms(0, 0, 0)), + // does not apply to other types + _ => Err(()), + } + } + + // https://html.spec.whatwg.org/multipage/#concept-input-value-date-string + // This does the safe Rust part of conversion; the unsafe JS Date part + // is in SetValueAsDate + fn convert_naive_datetime_to_string(&self, value: NaiveDateTime) -> Result<DOMString, ()> { + match self.input_type() { + InputType::Date => Ok(DOMString::from(value.format("%Y-%m-%d").to_string())), + InputType::Month => Ok(DOMString::from(value.format("%Y-%m").to_string())), + InputType::Week => { + let year = value.iso_week().year(); // not necessarily the same as value.year() + let week = value.iso_week().week(); + Ok(DOMString::from(format!("{:04}-W{:02}", year, week))) + }, + InputType::Time => Ok(DOMString::from(value.format("%H:%M:%S%.3f").to_string())), + // this won't be called from other input types + _ => unreachable!(), + } + } } impl VirtualMethods for HTMLInputElement { @@ -1889,3 +2514,21 @@ fn filter_from_accept(s: &DOMString) -> Vec<FilterPattern> { filter } + +fn round_halves_positive(n: f64) -> f64 { + // WHATWG specs about input steps say to round to the nearest step, + // rounding halves always to positive infinity. + // This differs from Rust's .round() in the case of -X.5. + if n.fract() == -0.5 { + n.ceil() + } else { + n.round() + } +} + +fn milliseconds_to_datetime(value: f64) -> Result<NaiveDateTime, ()> { + let seconds = (value / 1000.0).floor(); + let milliseconds = value - (seconds * 1000.0); + let nanoseconds = milliseconds * 1e6; + NaiveDateTime::from_timestamp_opt(seconds as i64, nanoseconds as u32).ok_or(()) +} diff --git a/components/script/dom/htmloptionelement.rs b/components/script/dom/htmloptionelement.rs index 4ebb2860a79..d61f4b6ffb0 100644 --- a/components/script/dom/htmloptionelement.rs +++ b/components/script/dom/htmloptionelement.rs @@ -28,6 +28,7 @@ use crate::dom::window::Window; use dom_struct::dom_struct; use html5ever::{LocalName, Prefix, QualName}; use std::cell::Cell; +use std::convert::TryInto; use style::element_state::ElementState; use style::str::{split_html_space_chars, str_join}; @@ -127,6 +128,41 @@ impl HTMLOptionElement { select.ask_for_reset(); } } + + // https://html.spec.whatwg.org/multipage/#concept-option-index + fn index(&self) -> i32 { + if let Some(parent) = self.upcast::<Node>().GetParentNode() { + if let Some(select_parent) = parent.downcast::<HTMLSelectElement>() { + // return index in parent select's list of options + return self.index_in_select(select_parent); + } else if parent.is::<HTMLOptGroupElement>() { + if let Some(grandparent) = parent.GetParentNode() { + if let Some(select_grandparent) = grandparent.downcast::<HTMLSelectElement>() { + // return index in grandparent select's list of options + return self.index_in_select(select_grandparent); + } + } + } + } + // "If the option element is not in a list of options, + // then the option element's index is zero." + // self is neither a child of a select, nor a grandchild of a select + // via an optgroup, so it is not in a list of options + 0 + } + + fn index_in_select(&self, select: &HTMLSelectElement) -> i32 { + match select.list_of_options().position(|n| &*n == self) { + Some(index) => index.try_into().unwrap_or(0), + None => { + // shouldn't happen but not worth a browser panic + warn!( + "HTMLOptionElement called index_in_select at a select that did not contain it" + ); + 0 + }, + } + } } // FIXME(ajeffrey): Provide a way of buffering DOMStrings other than using Strings @@ -225,6 +261,11 @@ impl HTMLOptionElementMethods for HTMLOptionElement { self.selectedness.set(selected); self.pick_if_selected_and_reset(); } + + // https://html.spec.whatwg.org/multipage/#dom-option-index + fn Index(&self) -> i32 { + self.index() + } } impl VirtualMethods for HTMLOptionElement { diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs index f6330b9b65a..bef4bfff8b4 100755 --- a/components/script/dom/htmlselectelement.rs +++ b/components/script/dom/htmlselectelement.rs @@ -102,7 +102,7 @@ impl HTMLSelectElement { } // https://html.spec.whatwg.org/multipage/#concept-select-option-list - fn list_of_options(&self) -> impl Iterator<Item = DomRoot<HTMLOptionElement>> { + pub fn list_of_options(&self) -> impl Iterator<Item = DomRoot<HTMLOptionElement>> { self.upcast::<Node>().children().flat_map(|node| { if node.is::<HTMLOptionElement>() { let node = DomRoot::downcast::<HTMLOptionElement>(node).unwrap(); diff --git a/components/script/dom/identityhub.rs b/components/script/dom/identityhub.rs index 64e4cad3867..91543d2ea3f 100644 --- a/components/script/dom/identityhub.rs +++ b/components/script/dom/identityhub.rs @@ -5,7 +5,7 @@ use smallvec::SmallVec; use webgpu::wgpu::{ hub::IdentityManager, - id::{AdapterId, BufferId, DeviceId}, + id::{AdapterId, BindGroupLayoutId, BufferId, DeviceId}, Backend, }; @@ -14,6 +14,7 @@ pub struct IdentityHub { adapters: IdentityManager, devices: IdentityManager, buffers: IdentityManager, + bind_group_layouts: IdentityManager, backend: Backend, } @@ -23,6 +24,7 @@ impl IdentityHub { adapters: IdentityManager::default(), devices: IdentityManager::default(), buffers: IdentityManager::default(), + bind_group_layouts: IdentityManager::default(), backend, } } @@ -35,9 +37,13 @@ impl IdentityHub { self.devices.alloc(self.backend) } - pub fn create_buffer_id(&mut self) -> BufferId { + fn create_buffer_id(&mut self) -> BufferId { self.buffers.alloc(self.backend) } + + fn create_bind_group_layout_id(&mut self) -> BindGroupLayoutId { + self.bind_group_layouts.alloc(self.backend) + } } #[derive(Debug)] @@ -119,4 +125,18 @@ impl Identities { _ => self.dummy_hub.create_buffer_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(), + } + } } diff --git a/components/script/dom/messageevent.rs b/components/script/dom/messageevent.rs index 0bb2ea1fd7a..54a6f2ee588 100644 --- a/components/script/dom/messageevent.rs +++ b/components/script/dom/messageevent.rs @@ -13,7 +13,7 @@ use crate::dom::bindings::reflector::reflect_dom_object; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::trace::RootedTraceableBox; -use crate::dom::bindings::utils::message_ports_to_frozen_array; +use crate::dom::bindings::utils::to_frozen_array; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; @@ -61,6 +61,8 @@ pub struct MessageEvent { source: DomRefCell<Option<SrcObject>>, lastEventId: DomRefCell<DOMString>, ports: DomRefCell<Vec<Dom<MessagePort>>>, + #[ignore_malloc_size_of = "mozjs"] + frozen_ports: DomRefCell<Option<Heap<JSVal>>>, } impl MessageEvent { @@ -82,6 +84,7 @@ impl MessageEvent { .map(|port| Dom::from_ref(&*port)) .collect(), ), + frozen_ports: DomRefCell::new(None), } } @@ -238,13 +241,24 @@ impl MessageEventMethods for MessageEvent { /// <https://html.spec.whatwg.org/multipage/#dom-messageevent-ports> fn Ports(&self, cx: JSContext) -> JSVal { + if let Some(ports) = &*self.frozen_ports.borrow() { + return ports.get(); + } + let ports: Vec<DomRoot<MessagePort>> = self .ports .borrow() .iter() .map(|port| DomRoot::from_ref(&**port)) .collect(); - message_ports_to_frozen_array(ports.as_slice(), cx) + let frozen_ports = to_frozen_array(ports.as_slice(), cx); + + // Cache the Js value. + let heap_val = Heap::default(); + heap_val.set(frozen_ports); + *self.frozen_ports.borrow_mut() = Some(heap_val); + + frozen_ports } /// <https://html.spec.whatwg.org/multipage/#dom-messageevent-initmessageevent> @@ -268,6 +282,7 @@ impl MessageEventMethods for MessageEvent { .into_iter() .map(|port| Dom::from_ref(&*port)) .collect(); + *self.frozen_ports.borrow_mut() = None; self.event .init_event(Atom::from(type_), bubbles, cancelable); } diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 0fdf4a80b69..08f1f0812fb 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -318,9 +318,11 @@ pub mod gamepadlist; pub mod globalscope; pub mod gpu; pub mod gpuadapter; +pub mod gpubindgrouplayout; pub mod gpubuffer; pub mod gpubufferusage; pub mod gpudevice; +pub mod gpushaderstage; pub mod hashchangeevent; pub mod headers; pub mod history; diff --git a/components/script/dom/navigator.rs b/components/script/dom/navigator.rs index 03c6b27d156..3d04433bfcd 100644 --- a/components/script/dom/navigator.rs +++ b/components/script/dom/navigator.rs @@ -28,7 +28,7 @@ use smallvec::SmallVec; use std::cell::RefCell; use std::rc::Rc; use webgpu::wgpu::{ - id::{AdapterId, BufferId, DeviceId}, + id::{AdapterId, BindGroupLayoutId, BufferId, DeviceId}, Backend, }; @@ -88,6 +88,12 @@ impl Navigator { pub fn create_buffer_id(&self, backend: Backend) -> BufferId { self.gpu_id_hub.borrow_mut().create_buffer_id(backend) } + + pub fn create_bind_group_layout_id(&self, backend: Backend) -> BindGroupLayoutId { + self.gpu_id_hub + .borrow_mut() + .create_bind_group_layout_id(backend) + } } impl NavigatorMethods for Navigator { diff --git a/components/script/dom/nodelist.rs b/components/script/dom/nodelist.rs index 88fdd4cb70c..1fdcef4b340 100644 --- a/components/script/dom/nodelist.rs +++ b/components/script/dom/nodelist.rs @@ -7,7 +7,9 @@ use crate::dom::bindings::codegen::Bindings::NodeListBinding; use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; +use crate::dom::bindings::str::DOMString; use crate::dom::htmlelement::HTMLElement; +use crate::dom::htmlformelement::HTMLFormElement; use crate::dom::node::{ChildrenMutation, Node}; use crate::dom::window::Window; use dom_struct::dom_struct; @@ -19,6 +21,7 @@ pub enum NodeListType { Simple(Vec<Dom<Node>>), Children(ChildrenList), Labels(LabelsList), + Radio(RadioList), } // https://dom.spec.whatwg.org/#interface-nodelist @@ -83,6 +86,7 @@ impl NodeListMethods for NodeList { NodeListType::Simple(ref elems) => elems.len() as u32, NodeListType::Children(ref list) => list.len(), NodeListType::Labels(ref list) => list.len(), + NodeListType::Radio(ref list) => list.len(), } } @@ -94,6 +98,7 @@ impl NodeListMethods for NodeList { .map(|node| DomRoot::from_ref(&**node)), NodeListType::Children(ref list) => list.item(index), NodeListType::Labels(ref list) => list.item(index), + NodeListType::Radio(ref list) => list.item(index), } } @@ -108,20 +113,22 @@ impl NodeList { if let NodeListType::Children(ref list) = self.list_type { list } else { - panic!("called as_children_list() on a simple node list") + panic!("called as_children_list() on a non-children node list") } } - pub fn as_simple_list(&self) -> &Vec<Dom<Node>> { - if let NodeListType::Simple(ref list) = self.list_type { + pub fn as_radio_list(&self) -> &RadioList { + if let NodeListType::Radio(ref list) = self.list_type { list } else { - panic!("called as_simple_list() on a children node list") + panic!("called as_radio_list() on a non-radio node list") } } pub fn iter<'a>(&'a self) -> impl Iterator<Item = DomRoot<Node>> + 'a { let len = self.Length(); + // There is room for optimization here in non-simple cases, + // as calling Item repeatedly on a live list can involve redundant work. (0..len).flat_map(move |i| self.Item(i)) } } @@ -328,7 +335,7 @@ impl ChildrenList { } } -// Labels lists: There might room for performance optimization +// Labels lists: There might be room for performance optimization // analogous to the ChildrenMutation case of a children list, // in which we can keep information from an older access live // if we know nothing has happened that would change it. @@ -357,3 +364,40 @@ impl LabelsList { self.element.label_at(index) } } + +// Radio node lists: There is room for performance improvement here; +// a form is already aware of changes to its set of controls, +// so a radio list can cache and cache-invalidate its contents +// just by hooking into what the form already knows without a +// separate mutation observer. FIXME #25482 +#[derive(Clone, Copy, JSTraceable, MallocSizeOf)] +pub enum RadioListMode { + ControlsExceptImageInputs, + Images, +} + +#[derive(JSTraceable, MallocSizeOf)] +#[unrooted_must_root_lint::must_root] +pub struct RadioList { + form: Dom<HTMLFormElement>, + mode: RadioListMode, + name: DOMString, +} + +impl RadioList { + pub fn new(form: &HTMLFormElement, mode: RadioListMode, name: DOMString) -> RadioList { + RadioList { + form: Dom::from_ref(form), + mode: mode, + name: name, + } + } + + pub fn len(&self) -> u32 { + self.form.count_for_radio_list(self.mode, &self.name) + } + + pub fn item(&self, index: u32) -> Option<DomRoot<Node>> { + self.form.nth_for_radio_list(index, self.mode, &self.name) + } +} diff --git a/components/script/dom/radionodelist.rs b/components/script/dom/radionodelist.rs index d178921056a..21b7d39772b 100644 --- a/components/script/dom/radionodelist.rs +++ b/components/script/dom/radionodelist.rs @@ -8,11 +8,12 @@ use crate::dom::bindings::codegen::Bindings::RadioNodeListBinding; use crate::dom::bindings::codegen::Bindings::RadioNodeListBinding::RadioNodeListMethods; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::reflect_dom_object; -use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; +use crate::dom::htmlformelement::HTMLFormElement; use crate::dom::htmlinputelement::{HTMLInputElement, InputType}; use crate::dom::node::Node; -use crate::dom::nodelist::{NodeList, NodeListType}; +use crate::dom::nodelist::{NodeList, NodeListType, RadioList, RadioListMode}; use crate::dom::window::Window; use dom_struct::dom_struct; @@ -38,18 +39,33 @@ impl RadioNodeList { ) } - pub fn new_simple_list<T>(window: &Window, iter: T) -> DomRoot<RadioNodeList> - where - T: Iterator<Item = DomRoot<Node>>, - { + pub fn new_controls_except_image_inputs( + window: &Window, + form: &HTMLFormElement, + name: DOMString, + ) -> DomRoot<RadioNodeList> { RadioNodeList::new( window, - NodeListType::Simple(iter.map(|r| Dom::from_ref(&*r)).collect()), + NodeListType::Radio(RadioList::new( + form, + RadioListMode::ControlsExceptImageInputs, + name, + )), ) } - // FIXME: This shouldn't need to be implemented here since NodeList (the parent of - // RadioNodeList) implements Length + pub fn new_images( + window: &Window, + form: &HTMLFormElement, + name: DOMString, + ) -> DomRoot<RadioNodeList> { + RadioNodeList::new( + window, + NodeListType::Radio(RadioList::new(form, RadioListMode::Images, name)), + ) + } + + // https://dom.spec.whatwg.org/#dom-nodelist-length // https://github.com/servo/servo/issues/5875 pub fn Length(&self) -> u32 { self.node_list.Length() @@ -60,7 +76,6 @@ impl RadioNodeListMethods for RadioNodeList { // https://html.spec.whatwg.org/multipage/#dom-radionodelist-value fn Value(&self) -> DOMString { self.upcast::<NodeList>() - .as_simple_list() .iter() .filter_map(|node| { // Step 1 @@ -85,7 +100,7 @@ impl RadioNodeListMethods for RadioNodeList { // https://html.spec.whatwg.org/multipage/#dom-radionodelist-value fn SetValue(&self, value: DOMString) { - for node in self.upcast::<NodeList>().as_simple_list().iter() { + for node in self.upcast::<NodeList>().iter() { // Step 1 if let Some(input) = node.downcast::<HTMLInputElement>() { match input.input_type() { diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index b968aa47274..40f3da3c78e 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -25,6 +25,7 @@ use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator}; use crate::dom::globalscope::GlobalScope; use crate::dom::htmlformelement::{FormControlElementHelpers, HTMLFormElement}; use crate::dom::htmlimageelement::HTMLImageElement; +use crate::dom::htmlinputelement::HTMLInputElement; use crate::dom::htmlscriptelement::{HTMLScriptElement, ScriptResult}; use crate::dom::htmltemplateelement::HTMLTemplateElement; use crate::dom::node::{Node, ShadowIncluding}; @@ -1244,13 +1245,32 @@ fn create_element_for_token( } else { CustomElementCreationMode::Asynchronous }; + let element = Element::create(name, is, document, creator, creation_mode); - // Step 8. + // https://html.spec.whatwg.org/multipage#the-input-element:value-sanitization-algorithm-3 + // says to invoke sanitization "when an input element is first created"; + // however, since sanitization requires content attributes to function, + // it can't mean that literally. + // Indeed, to make sanitization work correctly, we need to _not_ sanitize + // until after all content attributes have been added + + let maybe_input = element.downcast::<HTMLInputElement>(); + if let Some(input) = maybe_input { + input.disable_sanitization(); + } + + // Step 8 for attr in attrs { element.set_attribute_from_parser(attr.name, attr.value, None); } + // _now_ we can sanitize (and we sanitize now even if the "value" + // attribute isn't present!) + if let Some(input) = maybe_input { + input.enable_sanitization(); + } + // Step 9. if will_execute_script { // Steps 9.1 - 9.2. diff --git a/components/script/dom/webgl2renderingcontext.rs b/components/script/dom/webgl2renderingcontext.rs index d3279955576..af11e947eda 100644 --- a/components/script/dom/webgl2renderingcontext.rs +++ b/components/script/dom/webgl2renderingcontext.rs @@ -50,7 +50,7 @@ use js::jsapi::{JSObject, Type}; use js::jsval::{BooleanValue, DoubleValue, Int32Value, UInt32Value}; use js::jsval::{JSVal, NullValue, ObjectValue, UndefinedValue}; use js::rust::CustomAutoRooterGuard; -use js::typedarray::{ArrayBufferView, CreateWith, Uint32, Uint32Array}; +use js::typedarray::{ArrayBufferView, CreateWith, Float32, Uint32, Uint32Array}; use script_layout_interface::HTMLCanvasDataSource; use std::cell::Cell; use std::cmp; @@ -73,6 +73,7 @@ pub struct WebGL2RenderingContext { texture_pack_row_length: Cell<usize>, texture_pack_skip_pixels: Cell<usize>, texture_pack_skip_rows: Cell<usize>, + enable_rasterizer_discard: Cell<bool>, } fn typedarray_elem_size(typeid: Type) -> usize { @@ -126,6 +127,7 @@ impl WebGL2RenderingContext { texture_pack_row_length: Cell::new(0), texture_pack_skip_pixels: Cell::new(0), texture_pack_skip_rows: Cell::new(0), + enable_rasterizer_discard: Cell::new(false), }) } @@ -1019,12 +1021,24 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 fn Enable(&self, cap: u32) { - self.base.Enable(cap) + match cap { + constants::RASTERIZER_DISCARD => { + self.enable_rasterizer_discard.set(true); + self.base.send_command(WebGLCommand::Enable(cap)); + }, + _ => self.base.Enable(cap), + } } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 fn Disable(&self, cap: u32) { - self.base.Disable(cap) + match cap { + constants::RASTERIZER_DISCARD => { + self.enable_rasterizer_discard.set(false); + self.base.send_command(WebGLCommand::Disable(cap)); + }, + _ => self.base.Disable(cap), + } } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 @@ -1228,9 +1242,12 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { } // TODO: We could write this without IPC, recording the calls to `enable` and `disable`. - /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.2 fn IsEnabled(&self, cap: u32) -> bool { - self.base.IsEnabled(cap) + match cap { + constants::RASTERIZER_DISCARD => self.enable_rasterizer_discard.get(), + _ => self.base.IsEnabled(cap), + } } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 @@ -1754,8 +1771,11 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { location: Option<&WebGLUniformLocation>, transpose: bool, v: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, ) { - self.base.UniformMatrix2fv(location, transpose, v) + self.base + .UniformMatrix2fv(location, transpose, v, src_offset, src_length) } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -1764,8 +1784,11 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { location: Option<&WebGLUniformLocation>, transpose: bool, v: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, ) { - self.base.UniformMatrix3fv(location, transpose, v) + self.base + .UniformMatrix3fv(location, transpose, v, src_offset, src_length) } /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 @@ -1774,8 +1797,179 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { location: Option<&WebGLUniformLocation>, transpose: bool, v: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.base + .UniformMatrix4fv(location, transpose, v, src_offset, src_length) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + fn UniformMatrix3x2fv( + &self, + location: Option<&WebGLUniformLocation>, + transpose: bool, + val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.base.with_location(location, |location| { + match location.type_() { + constants::FLOAT_MAT3x2 => {}, + _ => return Err(InvalidOperation), + } + let val = self.base.uniform_matrix_section( + val, + src_offset, + src_length, + transpose, + 3 * 2, + location, + )?; + self.base + .send_command(WebGLCommand::UniformMatrix3x2fv(location.id(), val)); + Ok(()) + }); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + fn UniformMatrix4x2fv( + &self, + location: Option<&WebGLUniformLocation>, + transpose: bool, + val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.base.with_location(location, |location| { + match location.type_() { + constants::FLOAT_MAT4x2 => {}, + _ => return Err(InvalidOperation), + } + let val = self.base.uniform_matrix_section( + val, + src_offset, + src_length, + transpose, + 4 * 2, + location, + )?; + self.base + .send_command(WebGLCommand::UniformMatrix4x2fv(location.id(), val)); + Ok(()) + }); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + fn UniformMatrix2x3fv( + &self, + location: Option<&WebGLUniformLocation>, + transpose: bool, + val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.base.with_location(location, |location| { + match location.type_() { + constants::FLOAT_MAT2x3 => {}, + _ => return Err(InvalidOperation), + } + let val = self.base.uniform_matrix_section( + val, + src_offset, + src_length, + transpose, + 2 * 3, + location, + )?; + self.base + .send_command(WebGLCommand::UniformMatrix2x3fv(location.id(), val)); + Ok(()) + }); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + fn UniformMatrix4x3fv( + &self, + location: Option<&WebGLUniformLocation>, + transpose: bool, + val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.base.with_location(location, |location| { + match location.type_() { + constants::FLOAT_MAT4x3 => {}, + _ => return Err(InvalidOperation), + } + let val = self.base.uniform_matrix_section( + val, + src_offset, + src_length, + transpose, + 4 * 3, + location, + )?; + self.base + .send_command(WebGLCommand::UniformMatrix4x3fv(location.id(), val)); + Ok(()) + }); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + fn UniformMatrix2x4fv( + &self, + location: Option<&WebGLUniformLocation>, + transpose: bool, + val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, + ) { + self.base.with_location(location, |location| { + match location.type_() { + constants::FLOAT_MAT2x4 => {}, + _ => return Err(InvalidOperation), + } + let val = self.base.uniform_matrix_section( + val, + src_offset, + src_length, + transpose, + 2 * 4, + location, + )?; + self.base + .send_command(WebGLCommand::UniformMatrix2x4fv(location.id(), val)); + Ok(()) + }); + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 + fn UniformMatrix3x4fv( + &self, + location: Option<&WebGLUniformLocation>, + transpose: bool, + val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, ) { - self.base.UniformMatrix4fv(location, transpose, v) + self.base.with_location(location, |location| { + match location.type_() { + constants::FLOAT_MAT3x4 => {}, + _ => return Err(InvalidOperation), + } + let val = self.base.uniform_matrix_section( + val, + src_offset, + src_length, + transpose, + 3 * 4, + location, + )?; + self.base + .send_command(WebGLCommand::UniformMatrix3x4fv(location.id(), val)); + Ok(()) + }); } /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8 @@ -1807,6 +2001,42 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { constants::UNSIGNED_INT_VEC4 => unsafe { uniform_typed::<Uint32>(*cx, &uniform_get(triple, WebGLCommand::GetUniformUint4)) }, + constants::FLOAT_MAT2x3 => unsafe { + uniform_typed::<Float32>( + *cx, + &uniform_get(triple, WebGLCommand::GetUniformFloat2x3), + ) + }, + constants::FLOAT_MAT2x4 => unsafe { + uniform_typed::<Float32>( + *cx, + &uniform_get(triple, WebGLCommand::GetUniformFloat2x4), + ) + }, + constants::FLOAT_MAT3x2 => unsafe { + uniform_typed::<Float32>( + *cx, + &uniform_get(triple, WebGLCommand::GetUniformFloat3x2), + ) + }, + constants::FLOAT_MAT3x4 => unsafe { + uniform_typed::<Float32>( + *cx, + &uniform_get(triple, WebGLCommand::GetUniformFloat3x4), + ) + }, + constants::FLOAT_MAT4x2 => unsafe { + uniform_typed::<Float32>( + *cx, + &uniform_get(triple, WebGLCommand::GetUniformFloat4x2), + ) + }, + constants::FLOAT_MAT4x3 => unsafe { + uniform_typed::<Float32>( + *cx, + &uniform_get(triple, WebGLCommand::GetUniformFloat4x3), + ) + }, _ => self.base.GetUniform(cx, program, location), } } diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index fdd04297446..460f50aaada 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -1322,6 +1322,25 @@ impl WebGLRenderingContext { Ok(vec) } + + pub fn uniform_matrix_section( + &self, + vec: Float32ArrayOrUnrestrictedFloatSequence, + offset: u32, + length: u32, + transpose: bool, + uniform_size: usize, + uniform_location: &WebGLUniformLocation, + ) -> WebGLResult<Vec<f32>> { + let vec = match vec { + Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(), + Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v, + }; + if transpose { + return Err(InvalidValue); + } + self.uniform_vec_section::<f32>(vec, offset, length, uniform_size, uniform_location) + } } #[cfg(not(feature = "webgl_backtrace"))] @@ -3526,25 +3545,16 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { location: Option<&WebGLUniformLocation>, transpose: bool, val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, ) { self.with_location(location, |location| { match location.type_() { constants::FLOAT_MAT2 => {}, _ => return Err(InvalidOperation), } - let val = match val { - Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(), - Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v, - }; - if transpose { - return Err(InvalidValue); - } - if val.len() < 4 || val.len() % 4 != 0 { - return Err(InvalidValue); - } - if location.size().is_none() && val.len() != 4 { - return Err(InvalidOperation); - } + let val = + self.uniform_matrix_section(val, src_offset, src_length, transpose, 4, location)?; self.send_command(WebGLCommand::UniformMatrix2fv(location.id(), val)); Ok(()) }); @@ -3556,25 +3566,16 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { location: Option<&WebGLUniformLocation>, transpose: bool, val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, ) { self.with_location(location, |location| { match location.type_() { constants::FLOAT_MAT3 => {}, _ => return Err(InvalidOperation), } - let val = match val { - Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(), - Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v, - }; - if transpose { - return Err(InvalidValue); - } - if val.len() < 9 || val.len() % 9 != 0 { - return Err(InvalidValue); - } - if location.size().is_none() && val.len() != 9 { - return Err(InvalidOperation); - } + let val = + self.uniform_matrix_section(val, src_offset, src_length, transpose, 9, location)?; self.send_command(WebGLCommand::UniformMatrix3fv(location.id(), val)); Ok(()) }); @@ -3586,25 +3587,16 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { location: Option<&WebGLUniformLocation>, transpose: bool, val: Float32ArrayOrUnrestrictedFloatSequence, + src_offset: u32, + src_length: u32, ) { self.with_location(location, |location| { match location.type_() { constants::FLOAT_MAT4 => {}, _ => return Err(InvalidOperation), } - let val = match val { - Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(), - Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v, - }; - if transpose { - return Err(InvalidValue); - } - if val.len() < 16 || val.len() % 16 != 0 { - return Err(InvalidValue); - } - if location.size().is_none() && val.len() != 16 { - return Err(InvalidOperation); - } + let val = + self.uniform_matrix_section(val, src_offset, src_length, transpose, 16, location)?; self.send_command(WebGLCommand::UniformMatrix4fv(location.id(), val)); Ok(()) }); diff --git a/components/script/dom/webidls/GPUBindGroupLayout.webidl b/components/script/dom/webidls/GPUBindGroupLayout.webidl new file mode 100644 index 00000000000..930a1dd84cc --- /dev/null +++ b/components/script/dom/webidls/GPUBindGroupLayout.webidl @@ -0,0 +1,34 @@ +/* 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 GPUBindGroupLayout { +}; +GPUBindGroupLayout includes GPUObjectBase; + +dictionary GPUBindGroupLayoutDescriptor : GPUObjectDescriptorBase { + required sequence<GPUBindGroupLayoutBindings> bindings; +}; + +// Note: Servo codegen doesn't like the name `GPUBindGroupLayoutBinding` because it's already occupied +// dictionary GPUBindGroupLayoutBinding { +dictionary GPUBindGroupLayoutBindings { + required unsigned long binding; + required GPUShaderStageFlags visibility; + required GPUBindingType type; + //GPUTextureViewDimension textureDimension = "2d"; + //GPUTextureComponentType textureComponentType = "float"; + boolean multisampled = false; + boolean hasDynamicOffset = false; +}; + +enum GPUBindingType { + "uniform-buffer", + "storage-buffer", + "readonly-storage-buffer", + "sampler", + "sampled-texture", + "storage-texture" +}; diff --git a/components/script/dom/webidls/GPUDevice.webidl b/components/script/dom/webidls/GPUDevice.webidl index 965f7e1b7d5..512f9bf649f 100644 --- a/components/script/dom/webidls/GPUDevice.webidl +++ b/components/script/dom/webidls/GPUDevice.webidl @@ -11,12 +11,12 @@ 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); + /*GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor); GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor); GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor); diff --git a/components/script/dom/webidls/GPUShaderStage.webidl b/components/script/dom/webidls/GPUShaderStage.webidl new file mode 100644 index 00000000000..27fcb550cc3 --- /dev/null +++ b/components/script/dom/webidls/GPUShaderStage.webidl @@ -0,0 +1,13 @@ +/* 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/#typedefdef-gpushaderstageflags +[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"] +interface GPUShaderStage { + const GPUShaderStageFlags VERTEX = 0x1; + const GPUShaderStageFlags FRAGMENT = 0x2; + const GPUShaderStageFlags COMPUTE = 0x4; +}; + +typedef unsigned long GPUShaderStageFlags; diff --git a/components/script/dom/webidls/HTMLFormElement.webidl b/components/script/dom/webidls/HTMLFormElement.webidl index 13ffbf4dfe8..744f6d84ca9 100644 --- a/components/script/dom/webidls/HTMLFormElement.webidl +++ b/components/script/dom/webidls/HTMLFormElement.webidl @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ // https://html.spec.whatwg.org/multipage/#htmlformelement -[Exposed=Window] +[Exposed=Window, LegacyUnenumerableNamedProperties] interface HTMLFormElement : HTMLElement { [HTMLConstructor] constructor(); diff --git a/components/script/dom/webidls/HTMLInputElement.webidl b/components/script/dom/webidls/HTMLInputElement.webidl index 64ee9aa7c2f..f86e924b415 100644 --- a/components/script/dom/webidls/HTMLInputElement.webidl +++ b/components/script/dom/webidls/HTMLInputElement.webidl @@ -39,7 +39,7 @@ interface HTMLInputElement : HTMLElement { attribute boolean indeterminate; // [CEReactions] // attribute DOMString inputMode; - // readonly attribute HTMLElement? list; + readonly attribute HTMLElement? list; [CEReactions] attribute DOMString max; [CEReactions, SetterThrows] @@ -72,15 +72,15 @@ interface HTMLInputElement : HTMLElement { attribute DOMString defaultValue; [CEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString value; - // attribute Date? valueAsDate; - // attribute unrestricted double valueAsNumber; - // attribute double valueLow; - // attribute double valueHigh; + [SetterThrows] + attribute object? valueAsDate; + [SetterThrows] + attribute unrestricted double valueAsNumber; // [CEReactions] // attribute unsigned long width; - //void stepUp(optional long n = 1); - //void stepDown(optional long n = 1); + [Throws] void stepUp(optional long n = 1); + [Throws] void stepDown(optional long n = 1); //readonly attribute boolean willValidate; //readonly attribute ValidityState validity; diff --git a/components/script/dom/webidls/HTMLOptionElement.webidl b/components/script/dom/webidls/HTMLOptionElement.webidl index 65f37458295..c995070d0e7 100644 --- a/components/script/dom/webidls/HTMLOptionElement.webidl +++ b/components/script/dom/webidls/HTMLOptionElement.webidl @@ -22,5 +22,5 @@ interface HTMLOptionElement : HTMLElement { [CEReactions] attribute DOMString text; - // readonly attribute long index; + readonly attribute long index; }; diff --git a/components/script/dom/webidls/WebGL2RenderingContext.webidl b/components/script/dom/webidls/WebGL2RenderingContext.webidl index 68ee72db8b6..859bfaf33c7 100644 --- a/components/script/dom/webidls/WebGL2RenderingContext.webidl +++ b/components/script/dom/webidls/WebGL2RenderingContext.webidl @@ -439,26 +439,20 @@ interface mixin WebGL2RenderingContextBase void uniform4uiv(WebGLUniformLocation? location, Uint32List data, optional GLuint srcOffset = 0, optional GLuint srcLength = 0); - // void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, - // optional GLuint srcOffset = 0, optional GLuint srcLength = 0); - // void uniformMatrix3x2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, - // optional GLuint srcOffset = 0, optional GLuint srcLength = 0); - // void uniformMatrix4x2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, - // optional GLuint srcOffset = 0, optional GLuint srcLength = 0); - - // void uniformMatrix2x3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, - // optional GLuint srcOffset = 0, optional GLuint srcLength = 0); - // void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, - // optional GLuint srcOffset = 0, optional GLuint srcLength = 0); - // void uniformMatrix4x3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, - // optional GLuint srcOffset = 0, optional GLuint srcLength = 0); - - // void uniformMatrix2x4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, - // optional GLuint srcOffset = 0, optional GLuint srcLength = 0); - // void uniformMatrix3x4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, - // optional GLuint srcOffset = 0, optional GLuint srcLength = 0); - // void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, - // optional GLuint srcOffset = 0, optional GLuint srcLength = 0); + void uniformMatrix3x2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, + optional GLuint srcOffset = 0, optional GLuint srcLength = 0); + void uniformMatrix4x2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, + optional GLuint srcOffset = 0, optional GLuint srcLength = 0); + + void uniformMatrix2x3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, + optional GLuint srcOffset = 0, optional GLuint srcLength = 0); + void uniformMatrix4x3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, + optional GLuint srcOffset = 0, optional GLuint srcLength = 0); + + void uniformMatrix2x4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, + optional GLuint srcOffset = 0, optional GLuint srcLength = 0); + void uniformMatrix3x4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, + optional GLuint srcOffset = 0, optional GLuint srcLength = 0); /* Vertex attribs */ // void vertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w); diff --git a/components/script/dom/webidls/WebGLRenderingContext.webidl b/components/script/dom/webidls/WebGLRenderingContext.webidl index e56f08ccacf..a9af389b042 100644 --- a/components/script/dom/webidls/WebGLRenderingContext.webidl +++ b/components/script/dom/webidls/WebGLRenderingContext.webidl @@ -668,9 +668,12 @@ interface mixin WebGLRenderingContextBase void uniform4iv(WebGLUniformLocation? location, Int32List data, optional GLuint srcOffset = 0, optional GLuint srcLength = 0); - void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List value); - void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List value); - void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List value); + void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, + optional GLuint srcOffset = 0, optional GLuint srcLength = 0); + void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, + optional GLuint srcOffset = 0, optional GLuint srcLength = 0); + void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data, + optional GLuint srcOffset = 0, optional GLuint srcLength = 0); void useProgram(WebGLProgram? program); void validateProgram(WebGLProgram program); diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl index f2cdeeb18a5..afd5183de33 100644 --- a/components/script/dom/webidls/Window.webidl +++ b/components/script/dom/webidls/Window.webidl @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ // https://html.spec.whatwg.org/multipage/#window -[Global=Window, Exposed=Window] +[Global=Window, Exposed=Window /*, LegacyUnenumerableNamedProperties */] /*sealed*/ interface Window : GlobalScope { // the current browsing context [Unforgeable] readonly attribute WindowProxy window; diff --git a/components/script/dom/webidls/WindowOrWorkerGlobalScope.webidl b/components/script/dom/webidls/WindowOrWorkerGlobalScope.webidl index c798b56ab00..61f150f8e81 100644 --- a/components/script/dom/webidls/WindowOrWorkerGlobalScope.webidl +++ b/components/script/dom/webidls/WindowOrWorkerGlobalScope.webidl @@ -20,6 +20,9 @@ interface mixin WindowOrWorkerGlobalScope { long setInterval(TimerHandler handler, optional long timeout = 0, any... arguments); void clearInterval(optional long handle = 0); + // microtask queuing + void queueMicrotask(VoidFunction callback); + // ImageBitmap // Promise<ImageBitmap> createImageBitmap(ImageBitmapSource image, optional ImageBitmapOptions options); // Promise<ImageBitmap> createImageBitmap( diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index d48f16ea662..03102968cc4 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -11,6 +11,7 @@ use crate::dom::bindings::codegen::Bindings::HistoryBinding::HistoryBinding::His use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryListBinding::MediaQueryListMethods; use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionState; use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit; +use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction; use crate::dom::bindings::codegen::Bindings::WindowBinding::{ self, FrameRequestCallback, WindowMethods, WindowPostMessageOptions, }; @@ -135,7 +136,6 @@ use style::str::HTML_SPACE_CHARACTERS; use style::stylesheets::CssRuleType; use style_traits::{CSSPixel, DevicePixel, ParsingMode}; use url::Position; -use webgpu::WebGPU; use webrender_api::units::{DeviceIntPoint, DeviceIntSize, LayoutPixel}; use webrender_api::{DocumentId, ExternalScrollId}; use webvr_traits::WebVRMsg; @@ -267,10 +267,6 @@ pub struct Window { #[ignore_malloc_size_of = "channels are hard"] webgl_chan: Option<WebGLChan>, - #[ignore_malloc_size_of = "channels are hard"] - /// A handle for communicating messages to the WebGPU threads. - webgpu: Option<WebGPU>, - /// A handle for communicating messages to the webvr thread, if available. #[ignore_malloc_size_of = "channels are hard"] webvr_chan: Option<IpcSender<WebVRMsg>>, @@ -466,10 +462,6 @@ impl Window { .map(|chan| WebGLCommandSender::new(chan.clone(), self.get_event_loop_waker())) } - pub fn webgpu_channel(&self) -> Option<WebGPU> { - self.webgpu.clone() - } - pub fn webvr_thread(&self) -> Option<IpcSender<WebVRMsg>> { self.webvr_chan.clone() } @@ -871,6 +863,12 @@ impl WindowMethods for Window { self.ClearTimeout(handle); } + // https://html.spec.whatwg.org/multipage/#dom-queuemicrotask + fn QueueMicrotask(&self, callback: Rc<VoidFunction>) { + self.upcast::<GlobalScope>() + .queue_function_as_microtask(callback); + } + // https://html.spec.whatwg.org/multipage/#dom-window fn Window(&self) -> DomRoot<WindowProxy> { self.window_proxy() @@ -2213,7 +2211,6 @@ impl Window { navigation_start: u64, navigation_start_precise: u64, webgl_chan: Option<WebGLChan>, - webgpu: Option<WebGPU>, webvr_chan: Option<IpcSender<WebVRMsg>>, webxr_registry: webxr_api::Registry, microtask_queue: Rc<MicrotaskQueue>, @@ -2292,7 +2289,6 @@ impl Window { media_query_lists: DOMTracker::new(), test_runner: Default::default(), webgl_chan, - webgpu, webvr_chan, webxr_registry, permission_state_invocation_results: Default::default(), diff --git a/components/script/dom/windowproxy.rs b/components/script/dom/windowproxy.rs index 7fea1728c12..6aa0b9244cf 100644 --- a/components/script/dom/windowproxy.rs +++ b/components/script/dom/windowproxy.rs @@ -822,8 +822,7 @@ unsafe extern "C" fn getOwnPropertyDescriptor( assert!(desc.obj.is_null() || desc.obj == target.get()); if desc.obj == target.get() { - // FIXME(#11868) Should assign to desc.obj, desc.get() is a copy. - desc.get().obj = proxy.get(); + desc.obj = proxy.get(); } true diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index f29fd70f99f..0b8d7e33478 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -5,6 +5,7 @@ use crate::compartments::InCompartment; use crate::dom::bindings::cell::{DomRefCell, Ref}; use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit; +use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction; use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType; use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods; use crate::dom::bindings::codegen::UnionTypes::{RequestOrUSVString, StringOrFunction}; @@ -341,6 +342,12 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope { self.ClearTimeout(handle); } + // https://html.spec.whatwg.org/multipage/#dom-queuemicrotask + fn QueueMicrotask(&self, callback: Rc<VoidFunction>) { + self.upcast::<GlobalScope>() + .queue_function_as_microtask(callback); + } + #[allow(unrooted_must_root)] // https://fetch.spec.whatwg.org/#fetch-method fn Fetch( |