diff options
Diffstat (limited to 'components/script_bindings')
-rw-r--r-- | components/script_bindings/codegen/Bindings.conf | 12 | ||||
-rw-r--r-- | components/script_bindings/codegen/CodegenRust.py | 13 | ||||
-rw-r--r-- | components/script_bindings/error.rs | 2 | ||||
-rw-r--r-- | components/script_bindings/import.rs | 9 | ||||
-rw-r--r-- | components/script_bindings/root.rs | 2 | ||||
-rw-r--r-- | components/script_bindings/str.rs | 65 | ||||
-rw-r--r-- | components/script_bindings/utils.rs | 106 | ||||
-rw-r--r-- | components/script_bindings/webidls/CSSStyleSheet.webidl | 1 | ||||
-rw-r--r-- | components/script_bindings/webidls/HTMLScriptElement.webidl | 4 | ||||
-rw-r--r-- | components/script_bindings/webidls/Response.webidl | 1 | ||||
-rw-r--r-- | components/script_bindings/webidls/Window.webidl | 3 |
11 files changed, 181 insertions, 37 deletions
diff --git a/components/script_bindings/codegen/Bindings.conf b/components/script_bindings/codegen/Bindings.conf index f9ab745e4ea..c50bc31a7f5 100644 --- a/components/script_bindings/codegen/Bindings.conf +++ b/components/script_bindings/codegen/Bindings.conf @@ -416,7 +416,7 @@ DOMInterfaces = { }, 'HTMLScriptElement': { - 'canGc': ['SetAsync', 'SetCrossOrigin', 'SetText'] + 'canGc': ['SetAsync', 'SetCrossOrigin', 'SetSrc', 'SetText'] }, 'HTMLSelectElement': { @@ -551,7 +551,7 @@ DOMInterfaces = { }, 'Response': { - 'canGc': ['Error', 'Redirect', 'Clone', 'Text', 'Blob', 'FormData', 'Json', 'ArrayBuffer', 'Headers', 'Bytes'], + 'canGc': ['Error', 'Redirect', 'Clone', 'CreateFromJson', 'Text', 'Blob', 'FormData', 'Json', 'ArrayBuffer', 'Headers', 'Bytes'], }, 'RTCPeerConnection': { @@ -642,8 +642,8 @@ DOMInterfaces = { }, 'Window': { - 'canGc': ['Stop', 'Fetch', 'Scroll', 'Scroll_','ScrollBy', 'ScrollBy_', 'Stop', 'Fetch', 'Open', 'CreateImageBitmap', 'TrustedTypes'], - 'inRealms': ['Fetch', 'GetOpener'], + 'canGc': ['Stop', 'Fetch', 'Scroll', 'Scroll_','ScrollBy', 'ScrollBy_', 'Stop', 'Fetch', 'Open', 'CreateImageBitmap', 'TrustedTypes', 'WebdriverCallback', 'WebdriverException'], + 'inRealms': ['Fetch', 'GetOpener', 'WebdriverCallback', 'WebdriverException'], 'additionalTraits': ['crate::interfaces::WindowHelpers'], }, @@ -764,6 +764,10 @@ DOMInterfaces = { 'inRealms': ['Abort', 'Close', 'Write'], }, +'TransformStreamDefaultController': { + 'canGc': ['Enqueue', 'Error', 'Terminate'], +}, + 'WorkerNavigator': { 'canGc': ['Languages'], }, diff --git a/components/script_bindings/codegen/CodegenRust.py b/components/script_bindings/codegen/CodegenRust.py index 107e047ea3b..48f024be70f 100644 --- a/components/script_bindings/codegen/CodegenRust.py +++ b/components/script_bindings/codegen/CodegenRust.py @@ -2405,15 +2405,22 @@ class CGDOMJSClass(CGThing): "flags": "JSCLASS_FOREGROUND_FINALIZE", "name": str_to_cstr_ptr(self.descriptor.interface.identifier.name), "resolveHook": "None", + "mayResolveHook": "None", "slots": "1", "traceHook": f"{TRACE_HOOK_NAME}::<D>", } if self.descriptor.isGlobal(): assert not self.descriptor.weakReferenceable - args["enumerateHook"] = "Some(enumerate_global::<D>)" args["flags"] = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL | JSCLASS_FOREGROUND_FINALIZE" args["slots"] = "JSCLASS_GLOBAL_SLOT_COUNT + 1" - args["resolveHook"] = "Some(resolve_global::<D>)" + if self.descriptor.interface.getExtendedAttribute("NeedResolve"): + args["enumerateHook"] = "Some(enumerate_window::<D>)" + args["resolveHook"] = "Some(resolve_window::<D>)" + args["mayResolveHook"] = "Some(may_resolve_window::<D>)" + else: + args["enumerateHook"] = "Some(enumerate_global)" + args["resolveHook"] = "Some(resolve_global)" + args["mayResolveHook"] = "Some(may_resolve_global)" args["traceHook"] = "js::jsapi::JS_GlobalObjectTraceHook" elif self.descriptor.weakReferenceable: args["slots"] = "2" @@ -2427,7 +2434,7 @@ pub(crate) fn init_class_ops<D: DomTypes>() {{ enumerate: None, newEnumerate: {args['enumerateHook']}, resolve: {args['resolveHook']}, - mayResolve: None, + mayResolve: {args['mayResolveHook']}, finalize: Some({args['finalizeHook']}), call: None, construct: None, diff --git a/components/script_bindings/error.rs b/components/script_bindings/error.rs index 8424ff0fa95..a95d0b0b78c 100644 --- a/components/script_bindings/error.rs +++ b/components/script_bindings/error.rs @@ -59,6 +59,8 @@ pub enum Error { Data, /// OperationError DOMException Operation, + /// NotAllowedError DOMException + NotAllowed, /// TypeError JavaScript Error Type(String), diff --git a/components/script_bindings/import.rs b/components/script_bindings/import.rs index 65e9ee30e1d..16cc92f07bf 100644 --- a/components/script_bindings/import.rs +++ b/components/script_bindings/import.rs @@ -127,10 +127,11 @@ pub(crate) mod module { pub(crate) use crate::script_runtime::CanGc; pub(crate) use crate::utils::{ AsVoidPtr, DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, DOMClass, DOMJSClass, JSCLASS_DOM_GLOBAL, - ProtoOrIfaceArray, enumerate_global, exception_to_promise, generic_getter, - generic_lenient_getter, generic_lenient_setter, generic_method, generic_setter, - generic_static_promise_method, get_array_index_from_id, get_property_on_prototype, - has_property_on_prototype, resolve_global, trace_global, + ProtoOrIfaceArray, enumerate_global, enumerate_window, exception_to_promise, + generic_getter, generic_lenient_getter, generic_lenient_setter, generic_method, + generic_setter, generic_static_promise_method, get_array_index_from_id, + get_property_on_prototype, has_property_on_prototype, may_resolve_global, + may_resolve_window, resolve_global, resolve_window, trace_global, }; pub(crate) use crate::weakref::DOM_WEAK_SLOT; pub(crate) use crate::{JSTraceable, proxyhandler}; diff --git a/components/script_bindings/root.rs b/components/script_bindings/root.rs index 51bc979908f..3d0378f0df1 100644 --- a/components/script_bindings/root.rs +++ b/components/script_bindings/root.rs @@ -412,7 +412,7 @@ impl RootCollection { .rposition(|r| std::ptr::addr_eq(*r as *const (), object as *const ())) { Some(idx) => { - roots.remove(idx); + roots.swap_remove(idx); }, None => panic!("Can't remove a root that was never rooted!"), } diff --git a/components/script_bindings/str.rs b/components/script_bindings/str.rs index 09d48512f3e..0ef6e0c528a 100644 --- a/components/script_bindings/str.rs +++ b/components/script_bindings/str.rs @@ -10,14 +10,19 @@ use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::str::FromStr; use std::sync::LazyLock; -use std::{fmt, ops, str}; +use std::{fmt, ops, slice, str}; use cssparser::CowRcStr; use html5ever::{LocalName, Namespace}; +use js::rust::wrappers::ToJSON; +use js::rust::{HandleObject, HandleValue}; use num_traits::Zero; use regex::Regex; use stylo_atoms::Atom; +use crate::error::Error; +use crate::script_runtime::JSContext as SafeJSContext; + /// Encapsulates the IDL `ByteString` type. #[derive(Clone, Debug, Default, Eq, JSTraceable, MallocSizeOf, PartialEq)] pub struct ByteString(Vec<u8>); @@ -293,6 +298,64 @@ impl DOMString { } } +/// Because this converts to a DOMString it becomes UTF-8 encoded which is closer to +/// the spec definition of <https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-json-bytes> +/// but we generally do not operate on anything that is truly a WTF-16 string. +/// +/// <https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string> +pub fn serialize_jsval_to_json_utf8( + cx: SafeJSContext, + data: HandleValue, +) -> Result<DOMString, Error> { + #[repr(C)] + struct ToJSONCallbackData { + string: Option<String>, + } + + let mut out_str = ToJSONCallbackData { string: None }; + + #[allow(unsafe_code)] + unsafe extern "C" fn write_callback( + string: *const u16, + len: u32, + data: *mut std::ffi::c_void, + ) -> bool { + let data = data as *mut ToJSONCallbackData; + let string_chars = slice::from_raw_parts(string, len as usize); + (*data) + .string + .get_or_insert_with(Default::default) + .push_str(&String::from_utf16_lossy(string_chars)); + true + } + + // 1. Let result be ? Call(%JSON.stringify%, undefined, « value »). + unsafe { + let stringify_result = ToJSON( + *cx, + data, + HandleObject::null(), + HandleValue::null(), + Some(write_callback), + &mut out_str as *mut ToJSONCallbackData as *mut _, + ); + // Note: ToJSON returns false when a JS error is thrown, so we need to return + // JSFailed to propagate the raised exception + if !stringify_result { + return Err(Error::JSFailed); + } + } + + // 2. If result is undefined, then throw a TypeError. + // Note: ToJSON will not call the callback if the data cannot be serialized. + // 3. Assert: result is a string. + // 4. Return result. + out_str + .string + .map(Into::into) + .ok_or_else(|| Error::Type("unable to serialize JSON".to_owned())) +} + impl Borrow<str> for DOMString { #[inline] fn borrow(&self) -> &str { diff --git a/components/script_bindings/utils.rs b/components/script_bindings/utils.rs index 03a5783958e..fa4982b9904 100644 --- a/components/script_bindings/utils.rs +++ b/components/script_bindings/utils.rs @@ -18,9 +18,10 @@ use js::jsapi::{ GetLinearStringLength, GetNonCCWObjectGlobal, HandleId as RawHandleId, HandleObject as RawHandleObject, Heap, JS_AtomizeStringN, JS_ClearPendingException, JS_DeprecatedStringHasLatin1Chars, JS_GetLatin1StringCharsAndLength, JS_IsExceptionPending, - JS_IsGlobalObject, JS_NewEnumerateStandardClasses, JS_ResolveStandardClass, JSAtom, JSContext, - JSJitInfo, JSObject, JSTracer, MutableHandleIdVector as RawMutableHandleIdVector, - MutableHandleValue as RawMutableHandleValue, ObjectOpResult, StringIsArrayIndex, + JS_IsGlobalObject, JS_MayResolveStandardClass, JS_NewEnumerateStandardClasses, + JS_ResolveStandardClass, JSAtom, JSAtomState, JSContext, JSJitInfo, JSObject, JSTracer, + MutableHandleIdVector as RawMutableHandleIdVector, MutableHandleValue as RawMutableHandleValue, + ObjectOpResult, PropertyKey, StringIsArrayIndex, jsid, }; use js::jsid::StringId; use js::jsval::{JSVal, UndefinedValue}; @@ -30,7 +31,7 @@ use js::rust::wrappers::{ JS_SetPendingException, JS_SetProperty, }; use js::rust::{ - HandleId, HandleObject, HandleValue, MutableHandleValue, ToString, get_object_class, + HandleId, HandleObject, HandleValue, MutableHandleValue, Runtime, ToString, get_object_class, }; use js::{JS_CALLEE, rooted}; use malloc_size_of::MallocSizeOfOps; @@ -577,15 +578,25 @@ impl AsCCharPtrPtr for [u8] { /// Enumerate lazy properties of a global object. /// Modeled after <https://github.com/mozilla/gecko-dev/blob/3fd619f47/dom/bindings/BindingUtils.cpp#L2814> -/// and <https://github.com/mozilla/gecko-dev/blob/3fd619f47/dom/base/nsGlobalWindowInner.cpp#3297> -pub(crate) unsafe extern "C" fn enumerate_global<D: DomTypes>( +pub(crate) unsafe extern "C" fn enumerate_global( cx: *mut JSContext, obj: RawHandleObject, props: RawMutableHandleIdVector, enumerable_only: bool, ) -> bool { assert!(JS_IsGlobalObject(obj.get())); - if !JS_NewEnumerateStandardClasses(cx, obj, props, enumerable_only) { + JS_NewEnumerateStandardClasses(cx, obj, props, enumerable_only) +} + +/// Enumerate lazy properties of a global object that is a Window. +/// <https://github.com/mozilla/gecko-dev/blob/3fd619f47/dom/base/nsGlobalWindowInner.cpp#3297> +pub(crate) unsafe extern "C" fn enumerate_window<D: DomTypes>( + cx: *mut JSContext, + obj: RawHandleObject, + props: RawMutableHandleIdVector, + enumerable_only: bool, +) -> bool { + if !enumerate_global(cx, obj, props, enumerable_only) { return false; } @@ -610,34 +621,68 @@ pub(crate) unsafe extern "C" fn enumerate_global<D: DomTypes>( true } +/// Returns true if the resolve hook for this global may resolve the provided id. +/// <https://searchfox.org/mozilla-central/rev/f3c8c63a097b61bb1f01e13629b9514e09395947/dom/bindings/BindingUtils.cpp#2809> +/// <https://searchfox.org/mozilla-central/rev/f3c8c63a097b61bb1f01e13629b9514e09395947/js/public/Class.h#283-291> +pub(crate) unsafe extern "C" fn may_resolve_global( + names: *const JSAtomState, + id: PropertyKey, + maybe_obj: *mut JSObject, +) -> bool { + JS_MayResolveStandardClass(names, id, maybe_obj) +} + +/// Returns true if the resolve hook for this window may resolve the provided id. +/// <https://searchfox.org/mozilla-central/rev/f3c8c63a097b61bb1f01e13629b9514e09395947/dom/base/nsGlobalWindowInner.cpp#3275> +/// <https://searchfox.org/mozilla-central/rev/f3c8c63a097b61bb1f01e13629b9514e09395947/js/public/Class.h#283-291> +pub(crate) unsafe extern "C" fn may_resolve_window<D: DomTypes>( + names: *const JSAtomState, + id: PropertyKey, + maybe_obj: *mut JSObject, +) -> bool { + if may_resolve_global(names, id, maybe_obj) { + return true; + } + + let cx = Runtime::get() + .expect("There must be a JSContext active") + .as_ptr(); + let Ok(bytes) = latin1_bytes_from_id(cx, id) else { + return false; + }; + + <D as DomHelpers<D>>::interface_map().contains_key(bytes) +} + /// Resolve a lazy global property, for interface objects and named constructors. -pub(crate) unsafe extern "C" fn resolve_global<D: DomTypes>( +pub(crate) unsafe extern "C" fn resolve_global( cx: *mut JSContext, obj: RawHandleObject, id: RawHandleId, rval: *mut bool, ) -> bool { assert!(JS_IsGlobalObject(obj.get())); - if !JS_ResolveStandardClass(cx, obj, id, rval) { + JS_ResolveStandardClass(cx, obj, id, rval) +} + +/// Resolve a lazy global property for a Window global. +pub(crate) unsafe extern "C" fn resolve_window<D: DomTypes>( + cx: *mut JSContext, + obj: RawHandleObject, + id: RawHandleId, + rval: *mut bool, +) -> bool { + if !resolve_global(cx, obj, id, rval) { return false; } + if *rval { return true; } - if !id.is_string() { - *rval = false; - return true; - } - - let string = id.to_string(); - if !JS_DeprecatedStringHasLatin1Chars(string) { + let Ok(bytes) = latin1_bytes_from_id(cx, *id) else { *rval = false; return true; - } - let mut length = 0; - let ptr = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), string, &mut length); - assert!(!ptr.is_null()); - let bytes = slice::from_raw_parts(ptr, length); + }; if let Some(interface) = <D as DomHelpers<D>>::interface_map().get(bytes) { (interface.define)(SafeJSContext::from_ptr(cx), Handle::from_raw(obj)); @@ -647,3 +692,22 @@ pub(crate) unsafe extern "C" fn resolve_global<D: DomTypes>( } true } + +/// Returns a slice of bytes corresponding to the bytes in the provided string id. +/// Returns an error if the id is not a string, or the string contains non-latin1 characters. +/// # Safety +/// The slice is only valid as long as the original id is not garbage collected. +unsafe fn latin1_bytes_from_id(cx: *mut JSContext, id: jsid) -> Result<&'static [u8], ()> { + if !id.is_string() { + return Err(()); + } + + let string = id.to_string(); + if !JS_DeprecatedStringHasLatin1Chars(string) { + return Err(()); + } + let mut length = 0; + let ptr = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), string, &mut length); + assert!(!ptr.is_null()); + Ok(slice::from_raw_parts(ptr, length)) +} diff --git a/components/script_bindings/webidls/CSSStyleSheet.webidl b/components/script_bindings/webidls/CSSStyleSheet.webidl index 1241b5c2769..302e7433300 100644 --- a/components/script_bindings/webidls/CSSStyleSheet.webidl +++ b/components/script_bindings/webidls/CSSStyleSheet.webidl @@ -11,6 +11,7 @@ interface CSSStyleSheet : StyleSheet { [Throws, SameObject] readonly attribute CSSRuleList cssRules; [Throws] unsigned long insertRule(DOMString rule, optional unsigned long index = 0); [Throws] undefined deleteRule(unsigned long index); + [Throws] undefined replaceSync(USVString text); }; dictionary CSSStyleSheetInit { diff --git a/components/script_bindings/webidls/HTMLScriptElement.webidl b/components/script_bindings/webidls/HTMLScriptElement.webidl index b79382dbbb8..6f02bb3cf47 100644 --- a/components/script_bindings/webidls/HTMLScriptElement.webidl +++ b/components/script_bindings/webidls/HTMLScriptElement.webidl @@ -7,8 +7,8 @@ interface HTMLScriptElement : HTMLElement { [HTMLConstructor] constructor(); - [CEReactions] - attribute USVString src; + [CEReactions, SetterThrows] + attribute (TrustedScriptURL or USVString) src; [CEReactions] attribute DOMString type; [CEReactions] diff --git a/components/script_bindings/webidls/Response.webidl b/components/script_bindings/webidls/Response.webidl index 0ced0c13794..d37538d4b6b 100644 --- a/components/script_bindings/webidls/Response.webidl +++ b/components/script_bindings/webidls/Response.webidl @@ -9,6 +9,7 @@ interface Response { [Throws] constructor(optional BodyInit? body = null, optional ResponseInit init = {}); [NewObject] static Response error(); [NewObject, Throws] static Response redirect(USVString url, optional unsigned short status = 302); + [NewObject, Throws, BinaryName="createFromJson"] static Response json(any data, optional ResponseInit init = {}); readonly attribute ResponseType type; diff --git a/components/script_bindings/webidls/Window.webidl b/components/script_bindings/webidls/Window.webidl index c7b6dde617d..81c442b119f 100644 --- a/components/script_bindings/webidls/Window.webidl +++ b/components/script_bindings/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 /*, LegacyUnenumerableNamedProperties */] +[Global=Window, Exposed=Window, LegacyUnenumerableNamedProperties, NeedResolve] /*sealed*/ interface Window : GlobalScope { // the current browsing context [LegacyUnforgeable, CrossOriginReadable] readonly attribute WindowProxy window; @@ -148,6 +148,7 @@ partial interface Window { partial interface Window { // Shouldn't be public, but just to make things work for now undefined webdriverCallback(optional any result); + undefined webdriverException(optional any result); undefined webdriverTimeout(); }; |