aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/bindings/codegen/CodegenRust.py8
-rw-r--r--components/script/dom/bindings/str.rs283
-rw-r--r--components/script/dom/bindings/trace.rs4
-rw-r--r--components/script/dom/bindings/utils.rs9
-rw-r--r--components/script/dom/eventtarget.rs48
-rw-r--r--components/script/dom/extendablemessageevent.rs19
-rw-r--r--components/script/dom/globalscope.rs10
-rw-r--r--components/script/dom/gpu.rs44
-rw-r--r--components/script/dom/gpuadapter.rs31
-rw-r--r--components/script/dom/gpubindgrouplayout.rs75
-rw-r--r--components/script/dom/gpubuffer.rs11
-rw-r--r--components/script/dom/gpudevice.rs253
-rw-r--r--components/script/dom/gpushaderstage.rs11
-rw-r--r--components/script/dom/htmlformcontrolscollection.rs19
-rw-r--r--components/script/dom/htmlformelement.rs118
-rwxr-xr-xcomponents/script/dom/htmlinputelement.rs659
-rw-r--r--components/script/dom/htmloptionelement.rs41
-rwxr-xr-xcomponents/script/dom/htmlselectelement.rs2
-rw-r--r--components/script/dom/identityhub.rs24
-rw-r--r--components/script/dom/messageevent.rs19
-rw-r--r--components/script/dom/mod.rs2
-rw-r--r--components/script/dom/navigator.rs8
-rw-r--r--components/script/dom/nodelist.rs54
-rw-r--r--components/script/dom/radionodelist.rs37
-rw-r--r--components/script/dom/servoparser/mod.rs22
-rw-r--r--components/script/dom/webgl2renderingcontext.rs246
-rw-r--r--components/script/dom/webglrenderingcontext.rs70
-rw-r--r--components/script/dom/webidls/GPUBindGroupLayout.webidl34
-rw-r--r--components/script/dom/webidls/GPUDevice.webidl8
-rw-r--r--components/script/dom/webidls/GPUShaderStage.webidl13
-rw-r--r--components/script/dom/webidls/HTMLFormElement.webidl2
-rw-r--r--components/script/dom/webidls/HTMLInputElement.webidl14
-rw-r--r--components/script/dom/webidls/HTMLOptionElement.webidl2
-rw-r--r--components/script/dom/webidls/WebGL2RenderingContext.webidl34
-rw-r--r--components/script/dom/webidls/WebGLRenderingContext.webidl9
-rw-r--r--components/script/dom/webidls/Window.webidl2
-rw-r--r--components/script/dom/webidls/WindowOrWorkerGlobalScope.webidl3
-rw-r--r--components/script/dom/window.rs18
-rw-r--r--components/script/dom/windowproxy.rs3
-rw-r--r--components/script/dom/workerglobalscope.rs7
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(