Merge branch 'main' into outreachy-intern

Signed-off-by: Aniebiet Afia <54312052+aniebietafia@users.noreply.github.com>
This commit is contained in:
Aniebiet Afia 2025-05-14 13:01:33 +01:00 committed by GitHub
commit 2e9bc76b7d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5603 changed files with 188713 additions and 83849 deletions

View file

@ -2,7 +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 script_traits::StructuredSerializedData;
use constellation_traits::StructuredSerializedData;
use servo_url::ImmutableOrigin;
use crate::dom::bindings::refcounted::Trusted;

View file

@ -16,12 +16,17 @@ pub(crate) trait Activatable {
fn is_instance_activatable(&self) -> bool;
// https://dom.spec.whatwg.org/#eventtarget-legacy-pre-activation-behavior
fn legacy_pre_activation_behavior(&self) -> Option<InputActivationState> {
fn legacy_pre_activation_behavior(&self, _can_gc: CanGc) -> Option<InputActivationState> {
None
}
// https://dom.spec.whatwg.org/#eventtarget-legacy-canceled-activation-behavior
fn legacy_canceled_activation_behavior(&self, _state_before: Option<InputActivationState>) {}
fn legacy_canceled_activation_behavior(
&self,
_state_before: Option<InputActivationState>,
_can_gc: CanGc,
) {
}
// https://dom.spec.whatwg.org/#eventtarget-activation-behavior
// event and target are used only by HTMLAnchorElement, in the case

View file

@ -8,7 +8,7 @@ use std::mem;
use devtools_traits::AttrInfo;
use dom_struct::dom_struct;
use html5ever::{LocalName, Namespace, Prefix, namespace_url, ns};
use html5ever::{LocalName, Namespace, Prefix, local_name, ns};
use style::attr::{AttrIdentifier, AttrValue};
use style::values::GenericAtomIdent;
use stylo_atoms::Atom;
@ -112,10 +112,10 @@ impl AttrMethods<crate::DomTypeHolder> for Attr {
}
// https://dom.spec.whatwg.org/#dom-attr-value
fn SetValue(&self, value: DOMString) {
fn SetValue(&self, value: DOMString, can_gc: CanGc) {
if let Some(owner) = self.owner() {
let value = owner.parse_attribute(self.namespace(), self.local_name(), value);
self.set_value(value, &owner);
self.set_value(value, &owner, can_gc);
} else {
*self.value.borrow_mut() = AttrValue::String(value.into());
}
@ -153,7 +153,7 @@ impl AttrMethods<crate::DomTypeHolder> for Attr {
}
impl Attr {
pub(crate) fn set_value(&self, mut value: AttrValue, owner: &Element) {
pub(crate) fn set_value(&self, mut value: AttrValue, owner: &Element, can_gc: CanGc) {
let name = self.local_name().clone();
let namespace = self.namespace().clone();
let old_value = DOMString::from(&**self.value());
@ -179,9 +179,12 @@ impl Attr {
assert_eq!(Some(owner), self.owner().as_deref());
owner.will_mutate_attr(self);
self.swap_value(&mut value);
if *self.namespace() == ns!() {
vtable_for(owner.upcast())
.attribute_mutated(self, AttributeMutation::Set(Some(&value)));
if is_relevant_attribute(self.namespace(), self.local_name()) {
vtable_for(owner.upcast()).attribute_mutated(
self,
AttributeMutation::Set(Some(&value)),
can_gc,
);
}
}
@ -280,3 +283,9 @@ impl<'dom> AttrHelpersForLayout<'dom> for LayoutDom<'dom, Attr> {
&self.unsafe_get().identifier.namespace.0
}
}
/// A helper function to check if attribute is relevant.
pub(crate) fn is_relevant_attribute(namespace: &Namespace, local_name: &LocalName) -> bool {
// <https://svgwg.org/svg2-draft/linking.html#XLinkHrefAttribute>
namespace == &ns!() || (namespace == &ns!(xlink) && local_name == &local_name!("href"))
}

View file

@ -5,6 +5,9 @@
use std::cell::Cell;
use dom_struct::dom_struct;
use script_bindings::codegen::InheritTypes::{
AudioNodeTypeId, AudioScheduledSourceNodeTypeId, EventTargetTypeId,
};
use servo_media::audio::graph::NodeId;
use servo_media::audio::node::{
AudioNodeInit, AudioNodeMessage, ChannelCountMode as ServoMediaChannelCountMode, ChannelInfo,
@ -17,9 +20,6 @@ use crate::dom::baseaudiocontext::BaseAudioContext;
use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{
AudioNodeMethods, AudioNodeOptions, ChannelCountMode, ChannelInterpretation,
};
use crate::dom::bindings::codegen::InheritTypes::{
AudioNodeTypeId, AudioScheduledSourceNodeTypeId, EventTargetTypeId,
};
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::{Dom, DomRoot};

View file

@ -574,7 +574,7 @@ impl BaseAudioContextMethods<crate::DomTypeHolder> for BaseAudioContext {
.decode_audio_data(audio_data, callbacks);
} else {
// Step 3.
promise.reject_error(Error::DataClone, can_gc);
promise.reject_error(Error::DataClone(None), can_gc);
return promise;
}

View file

@ -34,9 +34,11 @@ use js::rust::{
CustomAutoRooterGuard, Handle, MutableHandleObject,
MutableHandleValue as SafeMutableHandleValue,
};
#[cfg(feature = "webgpu")]
use js::typedarray::{ArrayBuffer, HeapArrayBuffer};
use js::typedarray::{
ArrayBuffer, ArrayBufferU8, ArrayBufferView, ArrayBufferViewU8, CreateWith, HeapArrayBuffer,
TypedArray, TypedArrayElement, TypedArrayElementCreator,
ArrayBufferU8, ArrayBufferViewU8, CreateWith, TypedArray, TypedArrayElement,
TypedArrayElementCreator,
};
use crate::dom::bindings::error::{Error, Fallible};
@ -61,36 +63,25 @@ pub(crate) enum BufferSource {
ArrayBuffer(Box<Heap<*mut JSObject>>),
}
pub(crate) fn new_initialized_heap_buffer_source<T>(
init: HeapTypedArrayInit,
pub(crate) fn create_heap_buffer_source_with_length<T>(
cx: JSContext,
len: u32,
can_gc: CanGc,
) -> Result<HeapBufferSource<T>, ()>
) -> Fallible<HeapBufferSource<T>>
where
T: TypedArrayElement + TypedArrayElementCreator,
T::Element: Clone + Copy,
{
let heap_buffer_source = match init {
HeapTypedArrayInit::Buffer(buffer_source) => HeapBufferSource {
buffer_source,
phantom: PhantomData,
},
HeapTypedArrayInit::Info { len, cx } => {
rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
let typed_array_result =
create_buffer_source_with_length::<T>(cx, len as usize, array.handle_mut(), can_gc);
if typed_array_result.is_err() {
return Err(());
}
rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
let typed_array_result =
create_buffer_source_with_length::<T>(cx, len as usize, array.handle_mut(), can_gc);
if typed_array_result.is_err() {
return Err(Error::JSFailed);
}
HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(Heap::boxed(*array.handle())))
},
};
Ok(heap_buffer_source)
}
pub(crate) enum HeapTypedArrayInit {
Buffer(BufferSource),
Info { len: u32, cx: JSContext },
Ok(HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(
Heap::boxed(*array.handle()),
)))
}
pub(crate) struct HeapBufferSource<T> {
@ -129,11 +120,11 @@ where
}
pub(crate) fn from_view(
chunk: CustomAutoRooterGuard<ArrayBufferView>,
) -> HeapBufferSource<ArrayBufferViewU8> {
HeapBufferSource::<ArrayBufferViewU8>::new(BufferSource::ArrayBufferView(Heap::boxed(
unsafe { *chunk.underlying_object() },
)))
chunk: CustomAutoRooterGuard<TypedArray<T, *mut JSObject>>,
) -> HeapBufferSource<T> {
HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(Heap::boxed(unsafe {
*chunk.underlying_object()
})))
}
pub(crate) fn default() -> Self {

View file

@ -1,289 +0,0 @@
/* 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/. */
//! Base classes to work with IDL callbacks.
use std::default::Default;
use std::ffi::CString;
use std::mem::drop;
use std::rc::Rc;
use js::jsapi::{
AddRawValueRoot, EnterRealm, Heap, IsCallable, JSObject, LeaveRealm, Realm, RemoveRawValueRoot,
};
use js::jsval::{JSVal, ObjectValue, UndefinedValue};
use js::rust::wrappers::{JS_GetProperty, JS_WrapObject};
use js::rust::{MutableHandleValue, Runtime};
use crate::DomTypes;
use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
use crate::dom::bindings::error::{Error, Fallible, report_pending_exception};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::settings_stack::{GenericAutoEntryScript, GenericAutoIncumbentScript};
use crate::dom::bindings::utils::AsCCharPtrPtr;
use crate::dom::document::DocumentHelpers;
use crate::dom::globalscope::GlobalScopeHelpers;
use crate::realms::{InRealm, enter_realm};
use crate::script_runtime::{CanGc, JSContext};
/// The exception handling used for a call.
#[derive(Clone, Copy, PartialEq)]
pub(crate) enum ExceptionHandling {
/// Report any exception and don't throw it to the caller code.
Report,
/// Throw any exception to the caller code.
Rethrow,
}
/// A common base class for representing IDL callback function and
/// callback interface types.
#[derive(JSTraceable)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub(crate) struct CallbackObject<D: DomTypes> {
/// The underlying `JSObject`.
callback: Heap<*mut JSObject>,
permanent_js_root: Heap<JSVal>,
/// The ["callback context"], that is, the global to use as incumbent
/// global when calling the callback.
///
/// Looking at the WebIDL standard, it appears as though there would always
/// be a value here, but [sometimes] callback functions are created by
/// hand-waving without defining the value of the callback context, and
/// without any JavaScript code on the stack to grab an incumbent global
/// from.
///
/// ["callback context"]: https://heycam.github.io/webidl/#dfn-callback-context
/// [sometimes]: https://github.com/whatwg/html/issues/2248
incumbent: Option<Dom<D::GlobalScope>>,
}
impl<D: DomTypes> CallbackObject<D> {
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
// These are used by the bindings and do not need `default()` functions.
#[allow(clippy::new_without_default)]
fn new() -> Self {
Self {
callback: Heap::default(),
permanent_js_root: Heap::default(),
incumbent: D::GlobalScope::incumbent().map(|i| Dom::from_ref(&*i)),
}
}
pub(crate) fn get(&self) -> *mut JSObject {
self.callback.get()
}
#[allow(unsafe_code)]
unsafe fn init(&mut self, cx: JSContext, callback: *mut JSObject) {
self.callback.set(callback);
self.permanent_js_root.set(ObjectValue(callback));
assert!(AddRawValueRoot(
*cx,
self.permanent_js_root.get_unsafe(),
b"CallbackObject::root\n".as_c_char_ptr()
));
}
}
impl<D: DomTypes> Drop for CallbackObject<D> {
#[allow(unsafe_code)]
fn drop(&mut self) {
unsafe {
if let Some(cx) = Runtime::get() {
RemoveRawValueRoot(cx.as_ptr(), self.permanent_js_root.get_unsafe());
}
}
}
}
impl<D: DomTypes> PartialEq for CallbackObject<D> {
fn eq(&self, other: &CallbackObject<D>) -> bool {
self.callback.get() == other.callback.get()
}
}
/// A trait to be implemented by concrete IDL callback function and
/// callback interface types.
pub(crate) trait CallbackContainer<D: DomTypes> {
/// Create a new CallbackContainer object for the given `JSObject`.
unsafe fn new(cx: JSContext, callback: *mut JSObject) -> Rc<Self>;
/// Returns the underlying `CallbackObject`.
fn callback_holder(&self) -> &CallbackObject<D>;
/// Returns the underlying `JSObject`.
fn callback(&self) -> *mut JSObject {
self.callback_holder().get()
}
/// Returns the ["callback context"], that is, the global to use as
/// incumbent global when calling the callback.
///
/// ["callback context"]: https://heycam.github.io/webidl/#dfn-callback-context
fn incumbent(&self) -> Option<&D::GlobalScope> {
self.callback_holder().incumbent.as_deref()
}
}
/// A common base class for representing IDL callback function types.
#[derive(JSTraceable, PartialEq)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub(crate) struct CallbackFunction<D: DomTypes> {
object: CallbackObject<D>,
}
impl<D: DomTypes> CallbackFunction<D> {
/// Create a new `CallbackFunction` for this object.
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
// These are used by the bindings and do not need `default()` functions.
#[allow(clippy::new_without_default)]
pub(crate) fn new() -> Self {
Self {
object: CallbackObject::new(),
}
}
/// Returns the underlying `CallbackObject`.
pub(crate) fn callback_holder(&self) -> &CallbackObject<D> {
&self.object
}
/// Initialize the callback function with a value.
/// Should be called once this object is done moving.
pub(crate) unsafe fn init(&mut self, cx: JSContext, callback: *mut JSObject) {
self.object.init(cx, callback);
}
}
/// A common base class for representing IDL callback interface types.
#[derive(JSTraceable, PartialEq)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub(crate) struct CallbackInterface<D: DomTypes> {
object: CallbackObject<D>,
}
impl<D: DomTypes> CallbackInterface<D> {
/// Create a new CallbackInterface object for the given `JSObject`.
// These are used by the bindings and do not need `default()` functions.
#[allow(clippy::new_without_default)]
pub(crate) fn new() -> Self {
Self {
object: CallbackObject::new(),
}
}
/// Returns the underlying `CallbackObject`.
pub(crate) fn callback_holder(&self) -> &CallbackObject<D> {
&self.object
}
/// Initialize the callback function with a value.
/// Should be called once this object is done moving.
pub(crate) unsafe fn init(&mut self, cx: JSContext, callback: *mut JSObject) {
self.object.init(cx, callback);
}
/// Returns the property with the given `name`, if it is a callable object,
/// or an error otherwise.
pub(crate) fn get_callable_property(&self, cx: JSContext, name: &str) -> Fallible<JSVal> {
rooted!(in(*cx) let mut callable = UndefinedValue());
rooted!(in(*cx) let obj = self.callback_holder().get());
unsafe {
let c_name = CString::new(name).unwrap();
if !JS_GetProperty(*cx, obj.handle(), c_name.as_ptr(), callable.handle_mut()) {
return Err(Error::JSFailed);
}
if !callable.is_object() || !IsCallable(callable.to_object()) {
return Err(Error::Type(format!(
"The value of the {} property is not callable",
name
)));
}
}
Ok(callable.get())
}
}
pub(crate) use script_bindings::callback::ThisReflector;
/// Wraps the reflector for `p` into the realm of `cx`.
pub(crate) fn wrap_call_this_value<T: ThisReflector>(
cx: JSContext,
p: &T,
mut rval: MutableHandleValue,
) -> bool {
rooted!(in(*cx) let mut obj = p.jsobject());
assert!(!obj.is_null());
unsafe {
if !JS_WrapObject(*cx, obj.handle_mut()) {
return false;
}
}
rval.set(ObjectValue(*obj));
true
}
/// A class that performs whatever setup we need to safely make a call while
/// this class is on the stack. After `new` returns, the call is safe to make.
pub(crate) struct CallSetup<D: DomTypes> {
/// The global for reporting exceptions. This is the global object of the
/// (possibly wrapped) callback object.
exception_global: DomRoot<D::GlobalScope>,
/// The `JSContext` used for the call.
cx: JSContext,
/// The realm we were in before the call.
old_realm: *mut Realm,
/// The exception handling used for the call.
handling: ExceptionHandling,
/// <https://heycam.github.io/webidl/#es-invoking-callback-functions>
/// steps 8 and 18.2.
entry_script: Option<GenericAutoEntryScript<D>>,
/// <https://heycam.github.io/webidl/#es-invoking-callback-functions>
/// steps 9 and 18.1.
incumbent_script: Option<GenericAutoIncumbentScript<D>>,
}
impl<D: DomTypes> CallSetup<D> {
/// Performs the setup needed to make a call.
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn new<T: CallbackContainer<D>>(callback: &T, handling: ExceptionHandling) -> Self {
let global = unsafe { D::GlobalScope::from_object(callback.callback()) };
if let Some(window) = global.downcast::<D::Window>() {
window.Document().ensure_safe_to_run_script_or_layout();
}
let cx = D::GlobalScope::get_cx();
let aes = GenericAutoEntryScript::<D>::new(&global);
let ais = callback.incumbent().map(GenericAutoIncumbentScript::new);
CallSetup {
exception_global: global,
cx,
old_realm: unsafe { EnterRealm(*cx, callback.callback()) },
handling,
entry_script: Some(aes),
incumbent_script: ais,
}
}
/// Returns the `JSContext` used for the call.
pub(crate) fn get_context(&self) -> JSContext {
self.cx
}
}
impl<D: DomTypes> Drop for CallSetup<D> {
fn drop(&mut self) {
unsafe {
LeaveRealm(*self.cx, self.old_realm);
}
if self.handling == ExceptionHandling::Report {
let ar = enter_realm(&*self.exception_global);
report_pending_exception(self.cx, true, InRealm::Entered(&ar), CanGc::note());
}
drop(self.incumbent_script.take());
drop(self.entry_script.take().unwrap());
}
}

View file

@ -10,6 +10,7 @@ pub(crate) use std::cell::{Ref, RefCell, RefMut};
#[cfg(feature = "refcell_backtrace")]
pub(crate) use accountable_refcell::{Ref, RefCell, RefMut, ref_filter_map};
use malloc_size_of::{MallocConditionalSizeOf, MallocSizeOfOps};
#[cfg(not(feature = "refcell_backtrace"))]
pub(crate) use ref_filter_map::ref_filter_map;
@ -24,6 +25,12 @@ pub(crate) struct DomRefCell<T> {
value: RefCell<T>,
}
impl<T: MallocConditionalSizeOf> MallocConditionalSizeOf for DomRefCell<T> {
fn conditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
self.value.borrow().conditional_size_of(ops)
}
}
// Functionality specific to Servo's `DomRefCell` type
// ===================================================

View file

@ -5,12 +5,13 @@
use std::ptr;
use html5ever::interface::QualName;
use html5ever::{LocalName, local_name, namespace_url, ns};
use html5ever::{LocalName, local_name, ns};
use js::conversions::ToJSValConvertible;
use js::glue::{UnwrapObjectDynamic, UnwrapObjectStatic};
use js::jsapi::{CallArgs, CurrentGlobalOrNull, JSAutoRealm, JSObject};
use js::rust::wrappers::{JS_SetPrototype, JS_WrapObject};
use js::rust::{HandleObject, MutableHandleObject, MutableHandleValue};
use script_bindings::interface::get_desired_proto;
use super::utils::ProtoOrIfaceArray;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
@ -40,9 +41,8 @@ use crate::dom::bindings::codegen::Bindings::{
};
use crate::dom::bindings::codegen::PrototypeList;
use crate::dom::bindings::conversions::DerivedFrom;
use crate::dom::bindings::error::{Error, throw_constructor_without_new, throw_dom_exception};
use crate::dom::bindings::error::{Error, throw_dom_exception};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::interface::get_desired_proto;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
use crate::dom::create::create_native_html_element;
@ -55,7 +55,7 @@ use crate::script_runtime::{CanGc, JSContext, JSContext as SafeJSContext};
use crate::script_thread::ScriptThread;
/// <https://html.spec.whatwg.org/multipage/#htmlconstructor>
unsafe fn html_constructor(
fn html_constructor(
cx: JSContext,
global: &GlobalScope,
call_args: &CallArgs,
@ -75,7 +75,9 @@ unsafe fn html_constructor(
// The new_target might be a cross-compartment wrapper. Get the underlying object
// so we can do the spec's object-identity checks.
rooted!(in(*cx) let new_target_unwrapped = UnwrapObjectDynamic(call_args.new_target().to_object(), *cx, true));
rooted!(in(*cx) let new_target_unwrapped = unsafe {
UnwrapObjectDynamic(call_args.new_target().to_object(), *cx, true)
});
if new_target_unwrapped.is_null() {
throw_dom_exception(
cx,
@ -114,7 +116,7 @@ unsafe fn html_constructor(
// Step 4. Let isValue be null.
let mut is_value = None;
rooted!(in(*cx) let callee = UnwrapObjectStatic(call_args.callee()));
rooted!(in(*cx) let callee = unsafe { UnwrapObjectStatic(call_args.callee()) });
if callee.is_null() {
throw_dom_exception(cx, global, Error::Security, can_gc);
return Err(());
@ -123,7 +125,7 @@ unsafe fn html_constructor(
{
let _ac = JSAutoRealm::new(*cx, callee.get());
rooted!(in(*cx) let mut constructor = ptr::null_mut::<JSObject>());
rooted!(in(*cx) let global_object = CurrentGlobalOrNull(*cx));
rooted!(in(*cx) let global_object = unsafe { CurrentGlobalOrNull(*cx) });
// Step 5. If definition's local name is equal to definition's name
// (i.e., definition is for an autonomous custom element):
@ -233,13 +235,15 @@ unsafe fn html_constructor(
};
rooted!(in(*cx) let mut element = result.reflector().get_jsobject().get());
if !JS_WrapObject(*cx, element.handle_mut()) {
return Err(());
unsafe {
if !JS_WrapObject(*cx, element.handle_mut()) {
return Err(());
}
JS_SetPrototype(*cx, element.handle(), prototype.handle());
result.to_jsval(*cx, MutableHandleValue::from_raw(call_args.rval()));
}
JS_SetPrototype(*cx, element.handle(), prototype.handle());
result.to_jsval(*cx, MutableHandleValue::from_raw(call_args.rval()));
Ok(())
}
@ -395,7 +399,7 @@ pub(crate) fn push_new_element_queue() {
ScriptThread::push_new_element_queue();
}
pub(crate) unsafe fn call_html_constructor<T: DerivedFrom<Element> + DomObject>(
pub(crate) fn call_html_constructor<T: DerivedFrom<Element> + DomObject>(
cx: JSContext,
args: &CallArgs,
global: &GlobalScope,
@ -418,26 +422,3 @@ pub(crate) unsafe fn call_html_constructor<T: DerivedFrom<Element> + DomObject>(
)
.is_ok()
}
pub(crate) unsafe fn call_default_constructor<D: crate::DomTypes>(
cx: JSContext,
args: &CallArgs,
global: &D::GlobalScope,
proto_id: PrototypeList::ID,
ctor_name: &str,
creator: unsafe fn(JSContext, HandleObject, *mut ProtoOrIfaceArray),
constructor: impl FnOnce(JSContext, &CallArgs, &D::GlobalScope, HandleObject) -> bool,
) -> bool {
if !args.is_constructing() {
throw_constructor_without_new(cx, ctor_name);
return false;
}
rooted!(in(*cx) let mut desired_proto = ptr::null_mut::<JSObject>());
let proto_result = get_desired_proto(cx, args, proto_id, creator, desired_proto.handle_mut());
if proto_result.is_err() {
return false;
}
constructor(cx, args, global, desired_proto.handle())
}

View file

@ -37,120 +37,15 @@ use std::ffi;
pub(crate) use js::conversions::{
ConversionBehavior, ConversionResult, FromJSValConvertible, ToJSValConvertible,
};
use js::error::throw_type_error;
use js::glue::GetProxyReservedSlot;
use js::jsapi::{Heap, IsWindowProxy, JS_IsExceptionPending, JSContext, JSObject};
use js::jsapi::{JS_IsExceptionPending, JSContext, JSObject};
use js::jsval::UndefinedValue;
use js::rust::wrappers::{IsArrayObject, JS_GetProperty, JS_HasProperty};
use js::rust::{HandleObject, HandleValue, MutableHandleValue};
use num_traits::Float;
pub(crate) use script_bindings::conversions::*;
use js::rust::wrappers::{JS_GetProperty, JS_HasProperty};
use js::rust::{HandleObject, MutableHandleValue};
pub(crate) use script_bindings::conversions::{is_dom_proxy, *};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::trace::{JSTraceable, RootedTraceableBox};
use crate::dom::bindings::utils::DOMClass;
use crate::dom::filelist::FileList;
use crate::dom::htmlcollection::HTMLCollection;
use crate::dom::htmlformcontrolscollection::HTMLFormControlsCollection;
use crate::dom::htmloptionscollection::HTMLOptionsCollection;
use crate::dom::nodelist::NodeList;
impl<T: Float + ToJSValConvertible> ToJSValConvertible for Finite<T> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
let value = **self;
value.to_jsval(cx, rval);
}
}
impl<T: Float + FromJSValConvertible<Config = ()>> FromJSValConvertible for Finite<T> {
type Config = ();
unsafe fn from_jsval(
cx: *mut JSContext,
value: HandleValue,
option: (),
) -> Result<ConversionResult<Finite<T>>, ()> {
let result = match FromJSValConvertible::from_jsval(cx, value, option)? {
ConversionResult::Success(v) => v,
ConversionResult::Failure(error) => {
// FIXME(emilio): Why throwing instead of propagating the error?
throw_type_error(cx, &error);
return Err(());
},
};
match Finite::new(result) {
Some(v) => Ok(ConversionResult::Success(v)),
None => {
throw_type_error(cx, "this argument is not a finite floating-point value");
Err(())
},
}
}
}
impl<T: ToJSValConvertible + JSTraceable> ToJSValConvertible for RootedTraceableBox<T> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
let value = &**self;
value.to_jsval(cx, rval);
}
}
impl<T> FromJSValConvertible for RootedTraceableBox<Heap<T>>
where
T: FromJSValConvertible + js::rust::GCMethods + Copy,
Heap<T>: JSTraceable + Default,
{
type Config = T::Config;
unsafe fn from_jsval(
cx: *mut JSContext,
value: HandleValue,
config: Self::Config,
) -> Result<ConversionResult<Self>, ()> {
T::from_jsval(cx, value, config).map(|result| match result {
ConversionResult::Success(inner) => {
ConversionResult::Success(RootedTraceableBox::from_box(Heap::boxed(inner)))
},
ConversionResult::Failure(msg) => ConversionResult::Failure(msg),
})
}
}
pub(crate) use script_bindings::conversions::is_dom_proxy;
/// Get a `*const libc::c_void` for the given DOM object, unless it is a DOM
/// wrapper, and checking if the object is of the correct type.
///
/// Returns Err(()) if `obj` is a wrapper or if the object is not an object
/// for a DOM object of the given type (as defined by the proto_id and proto_depth).
#[inline]
unsafe fn private_from_proto_check_static(
obj: *mut JSObject,
proto_check: fn(&'static DOMClass) -> bool,
) -> Result<*const libc::c_void, ()> {
let dom_class = get_dom_class(obj).map_err(|_| ())?;
if proto_check(dom_class) {
trace!("good prototype");
Ok(private_from_object(obj))
} else {
trace!("bad prototype");
Err(())
}
}
/// Get a `*const T` for a DOM object accessible from a `JSObject`, where the DOM object
/// is guaranteed not to be a wrapper.
pub(crate) fn native_from_object_static<T>(obj: *mut JSObject) -> Result<*const T, ()>
where
T: DomObject + IDLInterface,
{
unsafe { private_from_proto_check_static(obj, T::derives).map(|ptr| ptr as *const T) }
}
/// Get a `DomRoot<T>` for the given DOM object, unwrapping any wrapper
/// around it first, and checking if the object is of the correct type.
@ -162,19 +57,7 @@ pub(crate) fn root_from_object_static<T>(obj: *mut JSObject) -> Result<DomRoot<T
where
T: DomObject + IDLInterface,
{
native_from_object_static(obj).map(|ptr| unsafe { DomRoot::from_ref(&*ptr) })
}
/// Get a `*const T` for a DOM object accessible from a `HandleValue`.
/// Caller is responsible for throwing a JS exception if needed in case of error.
pub(crate) fn native_from_handlevalue<T>(v: HandleValue, cx: *mut JSContext) -> Result<*const T, ()>
where
T: DomObject + IDLInterface,
{
if !v.get().is_object() {
return Err(());
}
unsafe { native_from_object(v.get().to_object(), cx) }
unsafe { native_from_object_static(obj).map(|ptr| DomRoot::from_ref(&*ptr)) }
}
/// Get a `DomRoot<T>` for a DOM object accessible from a `HandleObject`.
@ -188,40 +71,6 @@ where
unsafe { root_from_object(obj.get(), cx) }
}
/// Returns whether `value` is an array-like object (Array, FileList,
/// HTMLCollection, HTMLFormControlsCollection, HTMLOptionsCollection,
/// NodeList).
pub(crate) unsafe fn is_array_like(cx: *mut JSContext, value: HandleValue) -> bool {
let mut is_array = false;
assert!(IsArrayObject(cx, value, &mut is_array));
if is_array {
return true;
}
let object: *mut JSObject = match FromJSValConvertible::from_jsval(cx, value, ()).unwrap() {
ConversionResult::Success(object) => object,
_ => return false,
};
if root_from_object::<FileList>(object, cx).is_ok() {
return true;
}
if root_from_object::<HTMLCollection>(object, cx).is_ok() {
return true;
}
if root_from_object::<HTMLFormControlsCollection>(object, cx).is_ok() {
return true;
}
if root_from_object::<HTMLOptionsCollection>(object, cx).is_ok() {
return true;
}
if root_from_object::<NodeList>(object, cx).is_ok() {
return true;
}
false
}
/// Get a property from a JS object.
pub(crate) unsafe fn get_property_jsval(
cx: *mut JSContext,
@ -272,22 +121,3 @@ where
Err(()) => Err(Error::JSFailed),
}
}
/// Get a `DomRoot<T>` for a WindowProxy accessible from a `HandleValue`.
/// Caller is responsible for throwing a JS exception if needed in case of error.
pub(crate) unsafe fn windowproxy_from_handlevalue<D: crate::DomTypes>(
v: HandleValue,
_cx: *mut JSContext,
) -> Result<DomRoot<D::WindowProxy>, ()> {
if !v.get().is_object() {
return Err(());
}
let object = v.get().to_object();
if !IsWindowProxy(object) {
return Err(());
}
let mut value = UndefinedValue();
GetProxyReservedSlot(object, 0, &mut value);
let ptr = value.to_private() as *const D::WindowProxy;
Ok(DomRoot::from_ref(&*ptr))
}

View file

@ -20,7 +20,6 @@ pub(crate) use script_bindings::error::*;
#[cfg(feature = "js_backtrace")]
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::PrototypeList::proto_id_to_name;
use crate::dom::bindings::conversions::{
ConversionResult, FromJSValConvertible, ToJSValConvertible, root_from_object,
};
@ -71,7 +70,22 @@ pub(crate) fn throw_dom_exception(
Error::Abort => DOMErrorName::AbortError,
Error::Timeout => DOMErrorName::TimeoutError,
Error::InvalidNodeType => DOMErrorName::InvalidNodeTypeError,
Error::DataClone => DOMErrorName::DataCloneError,
Error::DataClone(message) => match message {
Some(custom_message) => unsafe {
assert!(!JS_IsExceptionPending(*cx));
let exception = DOMException::new_with_custom_message(
global,
DOMErrorName::DataCloneError,
custom_message,
can_gc,
);
rooted!(in(*cx) let mut thrown = UndefinedValue());
exception.to_jsval(*cx, thrown.handle_mut());
JS_SetPendingException(*cx, thrown.handle(), ExceptionStackBehavior::Capture);
return;
},
None => DOMErrorName::DataCloneError,
},
Error::NoModificationAllowed => DOMErrorName::NoModificationAllowedError,
Error::QuotaExceeded => DOMErrorName::QuotaExceededError,
Error::TypeMismatch => DOMErrorName::TypeMismatchError,
@ -79,6 +93,7 @@ pub(crate) fn throw_dom_exception(
Error::NotReadable => DOMErrorName::NotReadableError,
Error::Data => DOMErrorName::DataError,
Error::Operation => DOMErrorName::OperationError,
Error::NotAllowed => DOMErrorName::NotAllowedError,
Error::Type(message) => unsafe {
assert!(!JS_IsExceptionPending(*cx));
throw_type_error(*cx, &message);
@ -257,23 +272,6 @@ pub(crate) fn report_pending_exception(
}
}
/// Throw an exception to signal that a `JSObject` can not be converted to a
/// given DOM type.
pub(crate) fn throw_invalid_this(cx: SafeJSContext, proto_id: u16) {
debug_assert!(unsafe { !JS_IsExceptionPending(*cx) });
let error = format!(
"\"this\" object does not implement interface {}.",
proto_id_to_name(proto_id)
);
unsafe { throw_type_error(*cx, &error) };
}
pub(crate) fn throw_constructor_without_new(cx: SafeJSContext, name: &str) {
debug_assert!(unsafe { !JS_IsExceptionPending(*cx) });
let error = format!("{} constructor: 'new' is required", name);
unsafe { throw_type_error(*cx, &error) };
}
pub(crate) trait ErrorToJsval {
fn to_jsval(
self,

View file

@ -1,52 +0,0 @@
/* 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/. */
//! Generic finalizer implementations for DOM binding implementations.
use std::any::type_name;
use std::mem;
use js::glue::JS_GetReservedSlot;
use js::jsapi::JSObject;
use js::jsval::UndefinedValue;
use crate::dom::bindings::utils::finalize_global as do_finalize_global;
use crate::dom::bindings::weakref::{DOM_WEAK_SLOT, WeakBox, WeakReferenceable};
pub(crate) unsafe fn finalize_common<T>(this: *const T) {
if !this.is_null() {
// The pointer can be null if the object is the unforgeable holder of that interface.
let _ = Box::from_raw(this as *mut T);
}
debug!("{} finalize: {:p}", type_name::<T>(), this);
}
pub(crate) unsafe fn finalize_global<T>(obj: *mut JSObject, this: *const T) {
do_finalize_global(obj);
finalize_common::<T>(this);
}
pub(crate) unsafe fn finalize_weak_referenceable<T: WeakReferenceable>(
obj: *mut JSObject,
this: *const T,
) {
let mut slot = UndefinedValue();
JS_GetReservedSlot(obj, DOM_WEAK_SLOT, &mut slot);
let weak_box_ptr = slot.to_private() as *mut WeakBox<T>;
if !weak_box_ptr.is_null() {
let count = {
let weak_box = &*weak_box_ptr;
assert!(weak_box.value.get().is_some());
assert!(weak_box.count.get() > 0);
weak_box.value.set(None);
let count = weak_box.count.get() - 1;
weak_box.count.set(count);
count
};
if count == 0 {
mem::drop(Box::from_raw(weak_box_ptr));
}
}
finalize_common::<T>(this);
}

View file

@ -1,95 +0,0 @@
/* 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/. */
//! Machinery to conditionally expose things.
use js::rust::HandleObject;
use servo_config::prefs::get;
use crate::dom::bindings::codegen::InterfaceObjectMap;
use crate::dom::bindings::interface::is_exposed_in;
use crate::dom::globalscope::GlobalScope;
use crate::realms::{AlreadyInRealm, InRealm};
use crate::script_runtime::JSContext;
/// A container with a list of conditions.
pub(crate) struct Guard<T: Clone + Copy> {
conditions: &'static [Condition],
value: T,
}
impl<T: Clone + Copy> Guard<T> {
/// Construct a new guarded value.
pub(crate) const fn new(conditions: &'static [Condition], value: T) -> Self {
Guard { conditions, value }
}
/// Expose the value if the conditions are satisfied.
///
/// The passed handle is the object on which the value may be exposed.
pub(crate) fn expose(
&self,
cx: JSContext,
obj: HandleObject,
global: HandleObject,
) -> Option<T> {
let mut exposed_on_global = false;
let conditions_satisfied = self.conditions.iter().all(|c| match c {
Condition::Satisfied => {
exposed_on_global = true;
true
},
// If there are multiple Exposed conditions, we just need one of them to be true
Condition::Exposed(globals) => {
exposed_on_global |= is_exposed_in(global, *globals);
true
},
_ => c.is_satisfied(cx, obj, global),
});
if conditions_satisfied && exposed_on_global {
Some(self.value)
} else {
None
}
}
}
/// A condition to expose things.
#[derive(Clone, Copy)]
pub(crate) enum Condition {
/// The condition is satisfied if the function returns true.
Func(fn(JSContext, HandleObject) -> bool),
/// The condition is satisfied if the preference is set.
Pref(&'static str),
// The condition is satisfied if the interface is exposed in the global.
Exposed(InterfaceObjectMap::Globals),
SecureContext(),
/// The condition is always satisfied.
Satisfied,
}
fn is_secure_context(cx: JSContext) -> bool {
unsafe {
let in_realm_proof = AlreadyInRealm::assert_for_cx(JSContext::from_ptr(*cx));
GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)).is_secure_context()
}
}
impl Condition {
pub(crate) fn is_satisfied(
&self,
cx: JSContext,
obj: HandleObject,
global: HandleObject,
) -> bool {
match *self {
Condition::Pref(name) => get().get_value(name).try_into().unwrap_or(false),
Condition::Func(f) => f(cx, obj),
Condition::Exposed(globals) => is_exposed_in(global, globals),
Condition::SecureContext() => is_secure_context(cx),
Condition::Satisfied => true,
}
}
}

View file

@ -2,165 +2,24 @@
* 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/. */
#[allow(unused_imports)]
pub(crate) mod base {
pub(crate) use std::ptr;
pub(crate) use std::rc::Rc;
pub(crate) use js::error::throw_type_error;
pub(crate) use js::jsapi::{
CurrentGlobalOrNull, HandleValue as RawHandleValue, HandleValueArray, Heap, IsCallable,
JS_NewObject, JSContext, JSObject,
};
pub(crate) use js::jsval::{JSVal, NullValue, ObjectOrNullValue, ObjectValue, UndefinedValue};
pub(crate) use js::panic::maybe_resume_unwind;
pub(crate) use js::rust::wrappers::{Call, JS_WrapValue};
pub(crate) use js::rust::{HandleObject, HandleValue, MutableHandleObject, MutableHandleValue};
pub(crate) use js::rust::{HandleObject, MutableHandleObject};
pub(crate) use crate::dom::bindings::callback::{
CallSetup, CallbackContainer, CallbackFunction, CallbackInterface, CallbackObject,
ExceptionHandling, ThisReflector, wrap_call_this_value,
};
pub(crate) use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{
ChannelCountMode, ChannelCountModeValues, ChannelInterpretation,
ChannelInterpretationValues,
};
pub(crate) use crate::dom::bindings::codegen::DomTypes::DomTypes;
pub(crate) use crate::dom::bindings::codegen::{GenericUnionTypes, UnionTypes};
pub(crate) use crate::dom::bindings::conversions::{
ConversionBehavior, ConversionResult, FromJSValConvertible, StringificationBehavior,
ToJSValConvertible, root_from_handlevalue,
};
pub(crate) use crate::dom::bindings::error::Error::JSFailed;
pub(crate) use crate::dom::bindings::error::{Fallible, throw_dom_exception};
pub(crate) use crate::dom::bindings::num::Finite;
pub(crate) use crate::dom::bindings::proxyhandler::CrossOriginProperties;
pub(crate) use crate::dom::bindings::reflector::{DomGlobalGeneric, DomObject};
pub(crate) use crate::dom::bindings::root::DomRoot;
pub(crate) use crate::dom::bindings::str::{ByteString, DOMString, USVString};
pub(crate) use crate::dom::bindings::trace::RootedTraceableBox;
pub(crate) use crate::dom::bindings::utils::{
DomHelpers, ThreadUnsafeOnceLock, get_dictionary_property, set_dictionary_property,
};
pub(crate) use crate::dom::globalscope::{GlobalScope, GlobalScopeHelpers};
pub(crate) use crate::dom::promise::PromiseHelpers;
pub(crate) use crate::realms::{AlreadyInRealm, InRealm};
pub(crate) use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
}
#[allow(unused_imports)]
pub(crate) mod module {
pub(crate) use std::cmp;
pub(crate) use std::ffi::CString;
pub(crate) use std::ptr::NonNull;
pub(crate) use js::glue::{
CreateProxyHandler, GetProxyReservedSlot, JS_GetReservedSlot, ProxyTraps,
SetProxyReservedSlot,
};
pub(crate) use js::jsapi::{
__BindgenBitfieldUnit, CallArgs, GCContext, GetRealmErrorPrototype,
GetRealmFunctionPrototype, GetRealmIteratorPrototype, GetRealmObjectPrototype,
GetWellKnownSymbol, Handle as RawHandle, HandleId as RawHandleId,
HandleObject as RawHandleObject, JS_AtomizeAndPinString, JS_ForwardGetPropertyTo,
JS_GetPropertyDescriptorById, JS_HasPropertyById, JS_NewPlainObject, JS_SetReservedSlot,
JSAutoRealm, JSCLASS_FOREGROUND_FINALIZE, JSCLASS_RESERVED_SLOTS_SHIFT, JSClass,
JSClassOps, JSFunctionSpec, JSITER_HIDDEN, JSITER_OWNONLY, JSITER_SYMBOLS,
JSJitGetterCallArgs, JSJitInfo, JSJitInfo__bindgen_ty_1, JSJitInfo__bindgen_ty_2,
JSJitInfo__bindgen_ty_3, JSJitInfo_AliasSet, JSJitInfo_ArgType, JSJitInfo_OpType,
JSJitMethodCallArgs, JSJitSetterCallArgs, JSNativeWrapper, JSPROP_ENUMERATE,
JSPROP_PERMANENT, JSPROP_READONLY, JSPropertySpec, JSPropertySpec_Accessor,
JSPropertySpec_AccessorsOrValue, JSPropertySpec_AccessorsOrValue_Accessors,
JSPropertySpec_Kind, JSPropertySpec_Name, JSPropertySpec_ValueWrapper,
JSPropertySpec_ValueWrapper__bindgen_ty_1, JSPropertySpec_ValueWrapper_Type, JSTracer,
JSTypedMethodJitInfo, JSValueType, MutableHandle as RawMutableHandle,
MutableHandleIdVector as RawMutableHandleIdVector,
MutableHandleObject as RawMutableHandleObject, MutableHandleValue as RawMutableHandleValue,
ObjectOpResult, PropertyDescriptor, SymbolCode, UndefinedHandleValue, jsid,
};
pub(crate) use js::jsval::PrivateValue;
pub(crate) use js::panic::wrap_panic;
pub(crate) use js::rust::wrappers::{
AppendToIdVector, Call, GetPropertyKeys, JS_CopyOwnPropertiesAndPrivateFields,
JS_DefineProperty, JS_DefinePropertyById2, JS_GetProperty,
JS_InitializePropertiesFromCompatibleNativeObject, JS_NewObjectWithGivenProto,
JS_NewObjectWithoutMetadata, JS_SetImmutablePrototype, JS_SetProperty, JS_SetPrototype,
JS_WrapObject, NewProxyObject, RUST_INTERNED_STRING_TO_JSID, RUST_SYMBOL_TO_JSID,
int_to_jsid,
};
pub(crate) use js::rust::{
CustomAutoRooterGuard, GCMethods, Handle, MutableHandle, get_context_realm,
get_object_class, get_object_realm,
};
pub(crate) use js::typedarray::{
ArrayBuffer, ArrayBufferView, Float32Array, Float64Array, Uint8Array, Uint8ClampedArray,
};
pub(crate) use js::{
JS_CALLEE, JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL,
JSCLASS_RESERVED_SLOTS_MASK, jsapi, typedarray,
};
pub(crate) use script_bindings::constant::{ConstantSpec, ConstantVal};
pub(crate) use script_bindings::record::Record;
pub(crate) use servo_config::pref;
pub(crate) use script_bindings::codegen::PrototypeList;
pub(crate) use script_bindings::conversions::IDLInterface;
pub(crate) use script_bindings::utils::DOMClass;
pub(crate) use super::base::*;
pub(crate) use crate::dom::bindings::codegen::Bindings::AnalyserNodeBinding::AnalyserOptions;
pub(crate) use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{
AudioNode_Binding, ChannelCountMode, ChannelCountModeValues, ChannelInterpretation,
ChannelInterpretationValues,
};
pub(crate) use crate::dom::bindings::codegen::Bindings::EventTargetBinding::EventTarget_Binding;
pub(crate) use crate::dom::bindings::codegen::{
InterfaceObjectMap, PrototypeList, RegisterBindings,
};
pub(crate) use crate::dom::bindings::constructor::{
call_default_constructor, call_html_constructor, pop_current_element_queue,
push_new_element_queue,
};
pub(crate) use crate::dom::bindings::conversions::{
DOM_OBJECT_SLOT, IDLInterface, StringificationBehavior, ToJSValConvertible, is_array_like,
jsid_to_string, native_from_handlevalue, native_from_object_static,
};
pub(crate) use crate::dom::bindings::error::{
Error, ErrorResult, throw_constructor_without_new,
};
pub(crate) use crate::dom::bindings::finalize::{
finalize_common, finalize_global, finalize_weak_referenceable,
};
pub(crate) use crate::dom::bindings::guard::{Condition, Guard};
pub(crate) use crate::dom::bindings::inheritance::Castable;
pub(crate) use crate::dom::bindings::interface::{
ConstructorClassHook, InterfaceConstructorBehavior, NonCallbackInterfaceObjectClass,
ProtoOrIfaceIndex, create_callback_interface_object, create_global_object,
create_interface_prototype_object, create_named_constructors,
create_noncallback_interface_object, define_dom_interface, define_guarded_methods,
define_guarded_properties, get_desired_proto, get_per_interface_object_handle,
is_exposed_in,
};
pub(crate) use crate::dom::bindings::iterable::{Iterable, IteratorType};
pub(crate) use crate::dom::bindings::like::{Maplike, Setlike};
pub(crate) use crate::dom::bindings::namespace::{
NamespaceObjectClass, create_namespace_object,
};
pub(crate) use crate::dom::bindings::proxyhandler;
pub(crate) use crate::dom::bindings::proxyhandler::{
ensure_expando_object, get_expando_object, set_property_descriptor,
};
pub(crate) use crate::dom::bindings::iterable::IterableIterator;
pub(crate) use crate::dom::bindings::reflector::{
DomObjectIteratorWrap, DomObjectWrap, Reflector,
};
pub(crate) use crate::dom::bindings::root::{Dom, DomSlice, MaybeUnreflectedDom, Root};
pub(crate) use crate::dom::bindings::trace::JSTraceable;
pub(crate) use crate::dom::bindings::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,
};
pub(crate) use crate::dom::bindings::weakref::{DOM_WEAK_SLOT, WeakReferenceable};
pub(crate) use crate::dom::types::{AnalyserNode, AudioNode, BaseAudioContext, EventTarget};
pub(crate) use crate::mem::malloc_size_of_including_raw_self;
pub(crate) use crate::realms::{AlreadyInRealm, InRealm};
pub(crate) use crate::script_runtime::CanGc;
pub(crate) use crate::dom::bindings::root::{Dom, Root};
pub(crate) use crate::dom::bindings::weakref::WeakReferenceable;
}

View file

@ -3,11 +3,4 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
pub(crate) use script_bindings::codegen::InheritTypes::*;
pub(crate) use script_bindings::inheritance::Castable;
#[allow(missing_docs)]
pub(crate) trait HasParent {
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
type Parent;
fn as_parent(&self) -> &Self::Parent;
}
pub(crate) use script_bindings::inheritance::{Castable, HasParent};

View file

@ -1,707 +0,0 @@
/* 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/. */
//! Machinery to initialise interface prototype objects and interface objects.
use std::convert::TryFrom;
use std::ffi::CStr;
use std::ptr;
use js::error::throw_type_error;
use js::glue::UncheckedUnwrapObject;
use js::jsapi::JS::CompartmentIterResult;
use js::jsapi::{
CallArgs, CheckedUnwrapStatic, Compartment, CompartmentSpecifier, CurrentGlobalOrNull,
GetFunctionRealm, GetNonCCWObjectGlobal, GetRealmGlobalOrNull, GetWellKnownSymbol,
HandleObject as RawHandleObject, IsSharableCompartment, IsSystemCompartment,
JS_AtomizeAndPinString, JS_GetFunctionObject, JS_GetProperty, JS_IterateCompartments,
JS_NewFunction, JS_NewGlobalObject, JS_NewObject, JS_NewPlainObject, JS_NewStringCopyN,
JS_SetReservedSlot, JS_WrapObject, JSAutoRealm, JSClass, JSClassOps, JSContext,
JSFUN_CONSTRUCTOR, JSFunctionSpec, JSObject, JSPROP_PERMANENT, JSPROP_READONLY,
JSPROP_RESOLVING, JSPropertySpec, JSString, JSTracer, ObjectOps, OnNewGlobalHookOption,
SymbolCode, TrueHandleValue, Value, jsid,
};
use js::jsval::{JSVal, NullValue, PrivateValue};
use js::rust::wrappers::{
JS_DefineProperty, JS_DefineProperty3, JS_DefineProperty4, JS_DefineProperty5,
JS_DefinePropertyById5, JS_FireOnNewGlobalObject, JS_LinkConstructorAndPrototype,
JS_NewObjectWithGivenProto, RUST_SYMBOL_TO_JSID,
};
use js::rust::{
HandleObject, HandleValue, MutableHandleObject, RealmOptions, define_methods,
define_properties, get_object_class, is_dom_class, maybe_wrap_object,
};
use script_bindings::constant::{ConstantSpec, define_constants};
use servo_url::MutableOrigin;
use crate::dom::bindings::codegen::InterfaceObjectMap::Globals;
use crate::dom::bindings::codegen::PrototypeList;
use crate::dom::bindings::conversions::{DOM_OBJECT_SLOT, get_dom_class};
use crate::dom::bindings::guard::Guard;
use crate::dom::bindings::principals::ServoJSPrincipals;
use crate::dom::bindings::utils::{
DOM_PROTOTYPE_SLOT, DOMJSClass, JSCLASS_DOM_GLOBAL, ProtoOrIfaceArray, get_proto_or_iface_array,
};
use crate::dom::globalscope::GlobalScope;
use crate::realms::{AlreadyInRealm, InRealm};
use crate::script_runtime::JSContext as SafeJSContext;
/// The class of a non-callback interface object.
#[derive(Clone, Copy)]
pub(crate) struct NonCallbackInterfaceObjectClass {
/// The SpiderMonkey class structure.
pub(crate) _class: JSClass,
/// The prototype id of that interface, used in the hasInstance hook.
pub(crate) _proto_id: PrototypeList::ID,
/// The prototype depth of that interface, used in the hasInstance hook.
pub(crate) _proto_depth: u16,
/// The string representation of the object.
pub(crate) representation: &'static [u8],
}
unsafe impl Sync for NonCallbackInterfaceObjectClass {}
impl NonCallbackInterfaceObjectClass {
/// Create a new `NonCallbackInterfaceObjectClass` structure.
pub(crate) const fn new(
constructor_behavior: &'static InterfaceConstructorBehavior,
string_rep: &'static [u8],
proto_id: PrototypeList::ID,
proto_depth: u16,
) -> NonCallbackInterfaceObjectClass {
NonCallbackInterfaceObjectClass {
_class: JSClass {
name: c"Function".as_ptr(),
flags: 0,
cOps: &constructor_behavior.0,
spec: 0 as *const _,
ext: 0 as *const _,
oOps: &OBJECT_OPS,
},
_proto_id: proto_id,
_proto_depth: proto_depth,
representation: string_rep,
}
}
/// cast own reference to `JSClass` reference
pub(crate) fn as_jsclass(&self) -> &JSClass {
unsafe { &*(self as *const _ as *const JSClass) }
}
}
/// A constructor class hook.
pub(crate) type ConstructorClassHook =
unsafe extern "C" fn(cx: *mut JSContext, argc: u32, vp: *mut Value) -> bool;
/// The constructor behavior of a non-callback interface object.
pub(crate) struct InterfaceConstructorBehavior(JSClassOps);
impl InterfaceConstructorBehavior {
/// An interface constructor that unconditionally throws a type error.
pub(crate) const fn throw() -> Self {
InterfaceConstructorBehavior(JSClassOps {
addProperty: None,
delProperty: None,
enumerate: None,
newEnumerate: None,
resolve: None,
mayResolve: None,
finalize: None,
call: Some(invalid_constructor),
construct: Some(invalid_constructor),
trace: None,
})
}
/// An interface constructor that calls a native Rust function.
pub(crate) const fn call(hook: ConstructorClassHook) -> Self {
InterfaceConstructorBehavior(JSClassOps {
addProperty: None,
delProperty: None,
enumerate: None,
newEnumerate: None,
resolve: None,
mayResolve: None,
finalize: None,
call: Some(non_new_constructor),
construct: Some(hook),
trace: None,
})
}
}
/// A trace hook.
pub(crate) type TraceHook = unsafe extern "C" fn(trc: *mut JSTracer, obj: *mut JSObject);
/// Create a global object with the given class.
pub(crate) unsafe fn create_global_object(
cx: SafeJSContext,
class: &'static JSClass,
private: *const libc::c_void,
trace: TraceHook,
mut rval: MutableHandleObject,
origin: &MutableOrigin,
) {
assert!(rval.is_null());
let mut options = RealmOptions::default();
options.creationOptions_.traceGlobal_ = Some(trace);
options.creationOptions_.sharedMemoryAndAtomics_ = false;
select_compartment(cx, &mut options);
let principal = ServoJSPrincipals::new(origin);
rval.set(JS_NewGlobalObject(
*cx,
class,
principal.as_raw(),
OnNewGlobalHookOption::DontFireOnNewGlobalHook,
&*options,
));
assert!(!rval.is_null());
// Initialize the reserved slots before doing anything that can GC, to
// avoid getting trace hooks called on a partially initialized object.
let private_val = PrivateValue(private);
JS_SetReservedSlot(rval.get(), DOM_OBJECT_SLOT, &private_val);
let proto_array: Box<ProtoOrIfaceArray> =
Box::new([ptr::null_mut::<JSObject>(); PrototypeList::PROTO_OR_IFACE_LENGTH]);
let val = PrivateValue(Box::into_raw(proto_array) as *const libc::c_void);
JS_SetReservedSlot(rval.get(), DOM_PROTOTYPE_SLOT, &val);
let _ac = JSAutoRealm::new(*cx, rval.get());
JS_FireOnNewGlobalObject(*cx, rval.handle());
}
/// Choose the compartment to create a new global object in.
fn select_compartment(cx: SafeJSContext, options: &mut RealmOptions) {
type Data = *mut Compartment;
unsafe extern "C" fn callback(
_cx: *mut JSContext,
data: *mut libc::c_void,
compartment: *mut Compartment,
) -> CompartmentIterResult {
let data = data as *mut Data;
if !IsSharableCompartment(compartment) || IsSystemCompartment(compartment) {
return CompartmentIterResult::KeepGoing;
}
// Choose any sharable, non-system compartment in this context to allow
// same-agent documents to share JS and DOM objects.
*data = compartment;
CompartmentIterResult::Stop
}
let mut compartment: Data = ptr::null_mut();
unsafe {
JS_IterateCompartments(
*cx,
(&mut compartment) as *mut Data as *mut libc::c_void,
Some(callback),
);
}
if compartment.is_null() {
options.creationOptions_.compSpec_ = CompartmentSpecifier::NewCompartmentAndZone;
} else {
options.creationOptions_.compSpec_ = CompartmentSpecifier::ExistingCompartment;
options.creationOptions_.__bindgen_anon_1.comp_ = compartment;
}
}
/// Create and define the interface object of a callback interface.
pub(crate) fn create_callback_interface_object(
cx: SafeJSContext,
global: HandleObject,
constants: &[Guard<&[ConstantSpec]>],
name: &CStr,
mut rval: MutableHandleObject,
) {
assert!(!constants.is_empty());
unsafe {
rval.set(JS_NewObject(*cx, ptr::null()));
}
assert!(!rval.is_null());
define_guarded_constants(cx, rval.handle(), constants, global);
define_name(cx, rval.handle(), name);
define_on_global_object(cx, global, name, rval.handle());
}
/// Create the interface prototype object of a non-callback interface.
#[allow(clippy::too_many_arguments)]
pub(crate) fn create_interface_prototype_object(
cx: SafeJSContext,
global: HandleObject,
proto: HandleObject,
class: &'static JSClass,
regular_methods: &[Guard<&'static [JSFunctionSpec]>],
regular_properties: &[Guard<&'static [JSPropertySpec]>],
constants: &[Guard<&[ConstantSpec]>],
unscopable_names: &[&CStr],
mut rval: MutableHandleObject,
) {
create_object(
cx,
global,
proto,
class,
regular_methods,
regular_properties,
constants,
rval.reborrow(),
);
if !unscopable_names.is_empty() {
rooted!(in(*cx) let mut unscopable_obj = ptr::null_mut::<JSObject>());
create_unscopable_object(cx, unscopable_names, unscopable_obj.handle_mut());
unsafe {
let unscopable_symbol = GetWellKnownSymbol(*cx, SymbolCode::unscopables);
assert!(!unscopable_symbol.is_null());
rooted!(in(*cx) let mut unscopable_id: jsid);
RUST_SYMBOL_TO_JSID(unscopable_symbol, unscopable_id.handle_mut());
assert!(JS_DefinePropertyById5(
*cx,
rval.handle(),
unscopable_id.handle(),
unscopable_obj.handle(),
JSPROP_READONLY as u32
))
}
}
}
/// Create and define the interface object of a non-callback interface.
#[allow(clippy::too_many_arguments)]
pub(crate) fn create_noncallback_interface_object(
cx: SafeJSContext,
global: HandleObject,
proto: HandleObject,
class: &'static NonCallbackInterfaceObjectClass,
static_methods: &[Guard<&'static [JSFunctionSpec]>],
static_properties: &[Guard<&'static [JSPropertySpec]>],
constants: &[Guard<&[ConstantSpec]>],
interface_prototype_object: HandleObject,
name: &CStr,
length: u32,
legacy_window_alias_names: &[&CStr],
mut rval: MutableHandleObject,
) {
create_object(
cx,
global,
proto,
class.as_jsclass(),
static_methods,
static_properties,
constants,
rval.reborrow(),
);
unsafe {
assert!(JS_LinkConstructorAndPrototype(
*cx,
rval.handle(),
interface_prototype_object
));
}
define_name(cx, rval.handle(), name);
define_length(cx, rval.handle(), i32::try_from(length).expect("overflow"));
define_on_global_object(cx, global, name, rval.handle());
if is_exposed_in(global, Globals::WINDOW) {
for legacy_window_alias in legacy_window_alias_names {
define_on_global_object(cx, global, legacy_window_alias, rval.handle());
}
}
}
/// Create and define the named constructors of a non-callback interface.
pub(crate) fn create_named_constructors(
cx: SafeJSContext,
global: HandleObject,
named_constructors: &[(ConstructorClassHook, &CStr, u32)],
interface_prototype_object: HandleObject,
) {
rooted!(in(*cx) let mut constructor = ptr::null_mut::<JSObject>());
for &(native, name, arity) in named_constructors {
unsafe {
let fun = JS_NewFunction(*cx, Some(native), arity, JSFUN_CONSTRUCTOR, name.as_ptr());
assert!(!fun.is_null());
constructor.set(JS_GetFunctionObject(fun));
assert!(!constructor.is_null());
assert!(JS_DefineProperty3(
*cx,
constructor.handle(),
c"prototype".as_ptr(),
interface_prototype_object,
(JSPROP_PERMANENT | JSPROP_READONLY) as u32
));
}
define_on_global_object(cx, global, name, constructor.handle());
}
}
/// Create a new object with a unique type.
#[allow(clippy::too_many_arguments)]
pub(crate) fn create_object(
cx: SafeJSContext,
global: HandleObject,
proto: HandleObject,
class: &'static JSClass,
methods: &[Guard<&'static [JSFunctionSpec]>],
properties: &[Guard<&'static [JSPropertySpec]>],
constants: &[Guard<&[ConstantSpec]>],
mut rval: MutableHandleObject,
) {
unsafe {
rval.set(JS_NewObjectWithGivenProto(*cx, class, proto));
}
assert!(!rval.is_null());
define_guarded_methods(cx, rval.handle(), methods, global);
define_guarded_properties(cx, rval.handle(), properties, global);
define_guarded_constants(cx, rval.handle(), constants, global);
}
/// Conditionally define constants on an object.
pub(crate) fn define_guarded_constants(
cx: SafeJSContext,
obj: HandleObject,
constants: &[Guard<&[ConstantSpec]>],
global: HandleObject,
) {
for guard in constants {
if let Some(specs) = guard.expose(cx, obj, global) {
define_constants(cx, obj, specs);
}
}
}
/// Conditionally define methods on an object.
pub(crate) fn define_guarded_methods(
cx: SafeJSContext,
obj: HandleObject,
methods: &[Guard<&'static [JSFunctionSpec]>],
global: HandleObject,
) {
for guard in methods {
if let Some(specs) = guard.expose(cx, obj, global) {
unsafe {
define_methods(*cx, obj, specs).unwrap();
}
}
}
}
/// Conditionally define properties on an object.
pub(crate) fn define_guarded_properties(
cx: SafeJSContext,
obj: HandleObject,
properties: &[Guard<&'static [JSPropertySpec]>],
global: HandleObject,
) {
for guard in properties {
if let Some(specs) = guard.expose(cx, obj, global) {
unsafe {
define_properties(*cx, obj, specs).unwrap();
}
}
}
}
/// Returns whether an interface with exposure set given by `globals` should
/// be exposed in the global object `obj`.
pub(crate) fn is_exposed_in(object: HandleObject, globals: Globals) -> bool {
unsafe {
let unwrapped = UncheckedUnwrapObject(object.get(), /* stopAtWindowProxy = */ false);
let dom_class = get_dom_class(unwrapped).unwrap();
globals.contains(dom_class.global)
}
}
/// The navigator.servo api is only exposed to about: pages except about:blank
pub(crate) fn is_servo_internal(cx: SafeJSContext, _object: HandleObject) -> bool {
unsafe {
let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
let global_scope = GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof));
let url = global_scope.get_url();
url.scheme() == "about" && url.as_str() != "about:blank"
}
}
/// Define a property with a given name on the global object. Should be called
/// through the resolve hook.
pub(crate) fn define_on_global_object(
cx: SafeJSContext,
global: HandleObject,
name: &CStr,
obj: HandleObject,
) {
unsafe {
assert!(JS_DefineProperty3(
*cx,
global,
name.as_ptr(),
obj,
JSPROP_RESOLVING
));
}
}
const OBJECT_OPS: ObjectOps = ObjectOps {
lookupProperty: None,
defineProperty: None,
hasProperty: None,
getProperty: None,
setProperty: None,
getOwnPropertyDescriptor: None,
deleteProperty: None,
getElements: None,
funToString: Some(fun_to_string_hook),
};
unsafe extern "C" fn fun_to_string_hook(
cx: *mut JSContext,
obj: RawHandleObject,
_is_to_source: bool,
) -> *mut JSString {
let js_class = get_object_class(obj.get());
assert!(!js_class.is_null());
let repr = (*(js_class as *const NonCallbackInterfaceObjectClass)).representation;
assert!(!repr.is_empty());
let ret = JS_NewStringCopyN(cx, repr.as_ptr() as *const libc::c_char, repr.len());
assert!(!ret.is_null());
ret
}
fn create_unscopable_object(cx: SafeJSContext, names: &[&CStr], mut rval: MutableHandleObject) {
assert!(!names.is_empty());
assert!(rval.is_null());
unsafe {
rval.set(JS_NewPlainObject(*cx));
assert!(!rval.is_null());
for &name in names {
assert!(JS_DefineProperty(
*cx,
rval.handle(),
name.as_ptr(),
HandleValue::from_raw(TrueHandleValue),
JSPROP_READONLY as u32,
));
}
}
}
fn define_name(cx: SafeJSContext, obj: HandleObject, name: &CStr) {
unsafe {
rooted!(in(*cx) let name = JS_AtomizeAndPinString(*cx, name.as_ptr()));
assert!(!name.is_null());
assert!(JS_DefineProperty4(
*cx,
obj,
c"name".as_ptr(),
name.handle(),
JSPROP_READONLY as u32
));
}
}
fn define_length(cx: SafeJSContext, obj: HandleObject, length: i32) {
unsafe {
assert!(JS_DefineProperty5(
*cx,
obj,
c"length".as_ptr(),
length,
JSPROP_READONLY as u32
));
}
}
unsafe extern "C" fn invalid_constructor(
cx: *mut JSContext,
_argc: libc::c_uint,
_vp: *mut JSVal,
) -> bool {
throw_type_error(cx, "Illegal constructor.");
false
}
unsafe extern "C" fn non_new_constructor(
cx: *mut JSContext,
_argc: libc::c_uint,
_vp: *mut JSVal,
) -> bool {
throw_type_error(cx, "This constructor needs to be called with `new`.");
false
}
pub(crate) enum ProtoOrIfaceIndex {
ID(PrototypeList::ID),
Constructor(PrototypeList::Constructor),
}
impl From<ProtoOrIfaceIndex> for usize {
fn from(index: ProtoOrIfaceIndex) -> usize {
match index {
ProtoOrIfaceIndex::ID(id) => id as usize,
ProtoOrIfaceIndex::Constructor(constructor) => constructor as usize,
}
}
}
pub(crate) fn get_per_interface_object_handle(
cx: SafeJSContext,
global: HandleObject,
id: ProtoOrIfaceIndex,
creator: unsafe fn(SafeJSContext, HandleObject, *mut ProtoOrIfaceArray),
mut rval: MutableHandleObject,
) {
unsafe {
assert!(((*get_object_class(global.get())).flags & JSCLASS_DOM_GLOBAL) != 0);
/* Check to see whether the interface objects are already installed */
let proto_or_iface_array = get_proto_or_iface_array(global.get());
let index: usize = id.into();
rval.set((*proto_or_iface_array)[index]);
if !rval.get().is_null() {
return;
}
creator(cx, global, proto_or_iface_array);
rval.set((*proto_or_iface_array)[index]);
assert!(!rval.get().is_null());
}
}
pub(crate) fn define_dom_interface(
cx: SafeJSContext,
global: HandleObject,
id: ProtoOrIfaceIndex,
creator: unsafe fn(SafeJSContext, HandleObject, *mut ProtoOrIfaceArray),
enabled: fn(SafeJSContext, HandleObject) -> bool,
) {
assert!(!global.get().is_null());
if !enabled(cx, global) {
return;
}
rooted!(in(*cx) let mut proto = ptr::null_mut::<JSObject>());
get_per_interface_object_handle(cx, global, id, creator, proto.handle_mut());
assert!(!proto.is_null());
}
fn get_proto_id_for_new_target(new_target: HandleObject) -> Option<PrototypeList::ID> {
unsafe {
let new_target_class = get_object_class(*new_target);
if is_dom_class(&*new_target_class) {
let domjsclass: *const DOMJSClass = new_target_class as *const DOMJSClass;
let dom_class = &(*domjsclass).dom_class;
return Some(dom_class.interface_chain[dom_class.depth as usize]);
}
None
}
}
pub(crate) fn get_desired_proto(
cx: SafeJSContext,
args: &CallArgs,
proto_id: PrototypeList::ID,
creator: unsafe fn(SafeJSContext, HandleObject, *mut ProtoOrIfaceArray),
mut desired_proto: MutableHandleObject,
) -> Result<(), ()> {
unsafe {
// This basically implements
// https://heycam.github.io/webidl/#internally-create-a-new-object-implementing-the-interface
// step 3.
assert!(args.is_constructing());
// The desired prototype depends on the actual constructor that was invoked,
// which is passed to us as the newTarget in the callargs. We want to do
// something akin to the ES6 specification's GetProtototypeFromConstructor (so
// get .prototype on the newTarget, with a fallback to some sort of default).
// First, a fast path for the case when the the constructor is in fact one of
// our DOM constructors. This is safe because on those the "constructor"
// property is non-configurable and non-writable, so we don't have to do the
// slow JS_GetProperty call.
rooted!(in(*cx) let mut new_target = args.new_target().to_object());
rooted!(in(*cx) let original_new_target = *new_target);
// See whether we have a known DOM constructor here, such that we can take a
// fast path.
let target_proto_id = get_proto_id_for_new_target(new_target.handle()).or_else(|| {
// We might still have a cross-compartment wrapper for a known DOM
// constructor. CheckedUnwrapStatic is fine here, because we're looking for
// DOM constructors and those can't be cross-origin objects.
*new_target = CheckedUnwrapStatic(*new_target);
if !new_target.is_null() && *new_target != *original_new_target {
get_proto_id_for_new_target(new_target.handle())
} else {
None
}
});
if let Some(proto_id) = target_proto_id {
let global = GetNonCCWObjectGlobal(*new_target);
let proto_or_iface_cache = get_proto_or_iface_array(global);
desired_proto.set((*proto_or_iface_cache)[proto_id as usize]);
if *new_target != *original_new_target && !JS_WrapObject(*cx, desired_proto.into()) {
return Err(());
}
return Ok(());
}
// Slow path. This basically duplicates the ES6 spec's
// GetPrototypeFromConstructor except that instead of taking a string naming
// the fallback prototype we determine the fallback based on the proto id we
// were handed.
rooted!(in(*cx) let mut proto_val = NullValue());
if !JS_GetProperty(
*cx,
original_new_target.handle().into(),
c"prototype".as_ptr(),
proto_val.handle_mut().into(),
) {
return Err(());
}
if proto_val.is_object() {
desired_proto.set(proto_val.to_object());
return Ok(());
}
// Fall back to getting the proto for our given proto id in the realm that
// GetFunctionRealm(newTarget) returns.
let realm = GetFunctionRealm(*cx, new_target.handle().into());
if realm.is_null() {
return Err(());
}
{
let _realm = JSAutoRealm::new(*cx, GetRealmGlobalOrNull(realm));
rooted!(in(*cx) let global = CurrentGlobalOrNull(*cx));
get_per_interface_object_handle(
cx,
global.handle(),
ProtoOrIfaceIndex::ID(proto_id),
creator,
desired_proto.reborrow(),
);
if desired_proto.is_null() {
return Err(());
}
}
maybe_wrap_object(*cx, desired_proto);
Ok(())
}
}

View file

@ -1,179 +0,0 @@
/* 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/. */
#![allow(unsafe_code)]
//! Implementation of `iterable<...>` and `iterable<..., ...>` WebIDL declarations.
use std::cell::Cell;
use std::marker::PhantomData;
use std::ptr;
use std::ptr::NonNull;
use dom_struct::dom_struct;
use js::conversions::ToJSValConvertible;
use js::jsapi::{Heap, JSObject};
use js::jsval::UndefinedValue;
use js::rust::{HandleObject, HandleValue, MutableHandleObject};
use script_bindings::conversions::IDLInterface;
pub(crate) use script_bindings::iterable::*;
use script_bindings::utils::DOMClass;
use crate::DomTypes;
use crate::dom::bindings::codegen::Bindings::IterableIteratorBinding::{
IterableKeyAndValueResult, IterableKeyOrValueResult,
};
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::{
DomGlobalGeneric, DomObjectIteratorWrap, DomObjectWrap, Reflector, reflect_dom_object,
};
use crate::dom::bindings::root::{Dom, DomRoot, Root};
use crate::dom::bindings::trace::{JSTraceable, NoTrace, RootedTraceableBox};
use crate::realms::InRealm;
use crate::script_runtime::{CanGc, JSContext};
/// An iterator over the iterable entries of a given DOM interface.
#[dom_struct]
pub(crate) struct IterableIterator<
D: DomTypes,
T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>,
> {
reflector: Reflector,
iterable: Dom<T>,
type_: IteratorType,
index: Cell<u32>,
_marker: NoTrace<PhantomData<D>>,
}
impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable> IterableIterator<D, T> {
pub fn global_(&self, realm: InRealm) -> DomRoot<D::GlobalScope> {
<Self as DomGlobalGeneric<D>>::global_(self, realm)
}
}
impl<
D: DomTypes,
T: DomObjectIteratorWrap<D>
+ JSTraceable
+ Iterable
+ DomGlobalGeneric<D>
+ IDLInterface
+ IteratorDerives,
> IDLInterface for IterableIterator<D, T>
{
fn derives(class: &'static DOMClass) -> bool {
<T as IteratorDerives>::derives(class)
}
}
impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
IterableIterator<D, T>
{
/// Create a new iterator instance for the provided iterable DOM interface.
pub(crate) fn new(iterable: &T, type_: IteratorType, realm: InRealm) -> DomRoot<Self> {
let iterator = Box::new(IterableIterator {
reflector: Reflector::new(),
type_,
iterable: Dom::from_ref(iterable),
index: Cell::new(0),
_marker: NoTrace(PhantomData),
});
reflect_dom_object(iterator, &*iterable.global_(realm), CanGc::note())
}
/// Return the next value from the iterable object.
#[allow(non_snake_case)]
pub(crate) fn Next(&self, cx: JSContext) -> Fallible<NonNull<JSObject>> {
let index = self.index.get();
rooted!(in(*cx) let mut value = UndefinedValue());
rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
let result = if index >= self.iterable.get_iterable_length() {
dict_return(cx, rval.handle_mut(), true, value.handle())
} else {
match self.type_ {
IteratorType::Keys => {
unsafe {
self.iterable
.get_key_at_index(index)
.to_jsval(*cx, value.handle_mut());
}
dict_return(cx, rval.handle_mut(), false, value.handle())
},
IteratorType::Values => {
unsafe {
self.iterable
.get_value_at_index(index)
.to_jsval(*cx, value.handle_mut());
}
dict_return(cx, rval.handle_mut(), false, value.handle())
},
IteratorType::Entries => {
rooted!(in(*cx) let mut key = UndefinedValue());
unsafe {
self.iterable
.get_key_at_index(index)
.to_jsval(*cx, key.handle_mut());
self.iterable
.get_value_at_index(index)
.to_jsval(*cx, value.handle_mut());
}
key_and_value_return(cx, rval.handle_mut(), key.handle(), value.handle())
},
}
};
self.index.set(index + 1);
result.map(|_| NonNull::new(rval.get()).expect("got a null pointer"))
}
}
impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
DomObjectWrap<D> for IterableIterator<D, T>
{
const WRAP: unsafe fn(
JSContext,
&D::GlobalScope,
Option<HandleObject>,
Box<Self>,
CanGc,
) -> Root<Dom<Self>> = T::ITER_WRAP;
}
fn dict_return(
cx: JSContext,
mut result: MutableHandleObject,
done: bool,
value: HandleValue,
) -> Fallible<()> {
let mut dict = IterableKeyOrValueResult::empty();
dict.done = done;
dict.value.set(value.get());
rooted!(in(*cx) let mut dict_value = UndefinedValue());
unsafe {
dict.to_jsval(*cx, dict_value.handle_mut());
}
result.set(dict_value.to_object());
Ok(())
}
fn key_and_value_return(
cx: JSContext,
mut result: MutableHandleObject,
key: HandleValue,
value: HandleValue,
) -> Fallible<()> {
let mut dict = IterableKeyAndValueResult::empty();
dict.done = false;
dict.value = Some(
vec![key, value]
.into_iter()
.map(|handle| RootedTraceableBox::from_box(Heap::boxed(handle.get())))
.collect(),
);
rooted!(in(*cx) let mut dict_value = UndefinedValue());
unsafe {
dict.to_jsval(*cx, dict_value.handle_mut());
}
result.set(dict_value.to_object());
Ok(())
}

View file

@ -135,23 +135,16 @@
#![deny(non_snake_case)]
pub(crate) mod buffer_source;
pub(crate) mod callback;
#[allow(dead_code)]
pub(crate) mod cell;
pub(crate) mod constructor;
pub(crate) mod conversions;
pub(crate) mod error;
pub(crate) mod finalize;
pub(crate) mod frozenarray;
pub(crate) mod function;
pub(crate) mod guard;
pub(crate) mod import;
pub(crate) mod inheritance;
pub(crate) mod interface;
pub(crate) mod iterable;
pub(crate) mod like;
pub(crate) mod namespace;
pub(crate) mod num;
pub(crate) mod principals;
pub(crate) mod proxyhandler;
pub(crate) mod refcounted;
@ -167,55 +160,28 @@ pub(crate) mod utils;
pub(crate) mod weakref;
pub(crate) mod xmlname;
pub(crate) use script_bindings::{callback, iterable, num};
/// Generated JS-Rust bindings.
#[allow(missing_docs, non_snake_case)]
pub(crate) mod codegen {
pub(crate) mod DomTypeHolder {
include!(concat!(env!("BINDINGS_OUT_DIR"), "/DomTypeHolder.rs"));
}
pub(crate) mod DomTypes {
include!(concat!(env!("BINDINGS_OUT_DIR"), "/DomTypes.rs"));
}
#[allow(dead_code)]
pub(crate) mod GenericBindings {
include!(concat!(env!("BINDINGS_OUT_DIR"), "/Bindings/mod.rs"));
include!(concat!(env!("OUT_DIR"), "/DomTypeHolder.rs"));
}
pub(crate) use script_bindings::codegen::GenericBindings;
#[allow(dead_code)]
pub(crate) mod Bindings {
include!(concat!(
env!("BINDINGS_OUT_DIR"),
"/ConcreteBindings/mod.rs"
));
include!(concat!(env!("OUT_DIR"), "/ConcreteBindings/mod.rs"));
}
pub(crate) mod InterfaceObjectMap {
include!(concat!(env!("BINDINGS_OUT_DIR"), "/InterfaceObjectMap.rs"));
pub(crate) use script_bindings::codegen::Globals::Globals;
include!(concat!(env!("OUT_DIR"), "/InterfaceObjectMap.rs"));
}
pub(crate) use script_bindings::codegen::InheritTypes;
#[allow(dead_code)]
pub(crate) mod ConcreteInheritTypes {
include!(concat!(
env!("BINDINGS_OUT_DIR"),
"/ConcreteInheritTypes.rs"
));
}
pub(crate) use script_bindings::codegen::PrototypeList;
pub(crate) mod RegisterBindings {
include!(concat!(env!("BINDINGS_OUT_DIR"), "/RegisterBindings.rs"));
}
#[allow(
non_camel_case_types,
unused_imports,
unused_variables,
clippy::large_enum_variant,
clippy::upper_case_acronyms,
clippy::enum_variant_names
)]
pub(crate) mod GenericUnionTypes {
include!(concat!(env!("BINDINGS_OUT_DIR"), "/GenericUnionTypes.rs"));
include!(concat!(env!("OUT_DIR"), "/ConcreteInheritTypes.rs"));
}
pub(crate) use script_bindings::codegen::{PrototypeList, RegisterBindings};
#[allow(dead_code)]
pub(crate) mod UnionTypes {
include!(concat!(env!("BINDINGS_OUT_DIR"), "/UnionTypes.rs"));
include!(concat!(env!("OUT_DIR"), "/UnionTypes.rs"));
}
}

View file

@ -1,61 +0,0 @@
/* 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/. */
//! Machinery to initialise namespace objects.
use std::ffi::CStr;
use std::ptr;
use js::jsapi::{JSClass, JSFunctionSpec};
use js::rust::{HandleObject, MutableHandleObject};
use script_bindings::constant::ConstantSpec;
use crate::dom::bindings::guard::Guard;
use crate::dom::bindings::interface::{create_object, define_on_global_object};
use crate::script_runtime::JSContext;
/// The class of a namespace object.
#[derive(Clone, Copy)]
pub(crate) struct NamespaceObjectClass(JSClass);
unsafe impl Sync for NamespaceObjectClass {}
impl NamespaceObjectClass {
/// Create a new `NamespaceObjectClass` structure.
pub(crate) const unsafe fn new(name: &'static CStr) -> Self {
NamespaceObjectClass(JSClass {
name: name.as_ptr(),
flags: 0,
cOps: 0 as *mut _,
spec: ptr::null(),
ext: ptr::null(),
oOps: ptr::null(),
})
}
}
/// Create a new namespace object.
#[allow(clippy::too_many_arguments)]
pub(crate) fn create_namespace_object(
cx: JSContext,
global: HandleObject,
proto: HandleObject,
class: &'static NamespaceObjectClass,
methods: &[Guard<&'static [JSFunctionSpec]>],
constants: &[Guard<&'static [ConstantSpec]>],
name: &CStr,
mut rval: MutableHandleObject,
) {
create_object(
cx,
global,
proto,
&class.0,
methods,
&[],
constants,
rval.reborrow(),
);
define_on_global_object(cx, global, name, rval.handle());
}

View file

@ -1,57 +0,0 @@
/* 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/. */
//! The `Finite<T>` struct.
use std::default::Default;
use std::ops::Deref;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use num_traits::Float;
/// Encapsulates the IDL restricted float type.
#[derive(Clone, Copy, Eq, JSTraceable, PartialEq)]
pub(crate) struct Finite<T: Float>(T);
impl<T: Float> Finite<T> {
/// Create a new `Finite<T: Float>` safely.
pub(crate) fn new(value: T) -> Option<Finite<T>> {
if value.is_finite() {
Some(Finite(value))
} else {
None
}
}
/// Create a new `Finite<T: Float>`.
#[inline]
pub(crate) fn wrap(value: T) -> Finite<T> {
assert!(
value.is_finite(),
"Finite<T> doesn't encapsulate unrestricted value."
);
Finite(value)
}
}
impl<T: Float> Deref for Finite<T> {
type Target = T;
fn deref(&self) -> &T {
let Finite(value) = self;
value
}
}
impl<T: Float + MallocSizeOf> MallocSizeOf for Finite<T> {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
(**self).size_of(ops)
}
}
impl<T: Float + Default> Default for Finite<T> {
fn default() -> Finite<T> {
Finite::wrap(T::default())
}
}

View file

@ -2,127 +2,17 @@
* 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 std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ops::Deref;
use std::ptr::NonNull;
use js::glue::{
CreateRustJSPrincipals, DestroyRustJSPrincipals, GetRustJSPrincipalsPrivate,
JSPrincipalsCallbacks,
};
use js::glue::{DestroyRustJSPrincipals, GetRustJSPrincipalsPrivate, JSPrincipalsCallbacks};
use js::jsapi::{
JS_DropPrincipals, JS_HoldPrincipals, JS_ReadUint32Pair, JSContext, JSPrincipals,
JSStructuredCloneReader, JSStructuredCloneWriter,
JS_ReadUint32Pair, JSContext, JSPrincipals, JSStructuredCloneReader, JSStructuredCloneWriter,
};
use js::rust::Runtime;
use script_bindings::principals::{ServoJSPrincipals, ServoJSPrincipalsRef};
use servo_url::MutableOrigin;
use super::structuredclone::StructuredCloneTags;
/// An owned reference to Servo's `JSPrincipals` instance.
#[repr(transparent)]
pub(crate) struct ServoJSPrincipals(NonNull<JSPrincipals>);
impl ServoJSPrincipals {
pub(crate) fn new(origin: &MutableOrigin) -> Self {
unsafe {
let private: Box<MutableOrigin> = Box::new(origin.clone());
let raw = CreateRustJSPrincipals(&PRINCIPALS_CALLBACKS, Box::into_raw(private) as _);
// The created `JSPrincipals` object has an initial reference
// count of zero, so the following code will set it to one
Self::from_raw_nonnull(NonNull::new_unchecked(raw))
}
}
/// Construct `Self` from a raw `*mut JSPrincipals`, incrementing its
/// reference count.
#[inline]
pub(crate) unsafe fn from_raw_nonnull(raw: NonNull<JSPrincipals>) -> Self {
JS_HoldPrincipals(raw.as_ptr());
Self(raw)
}
#[inline]
pub(crate) unsafe fn origin(&self) -> MutableOrigin {
let origin = GetRustJSPrincipalsPrivate(self.0.as_ptr()) as *mut MutableOrigin;
(*origin).clone()
}
#[inline]
pub(crate) fn as_raw_nonnull(&self) -> NonNull<JSPrincipals> {
self.0
}
#[inline]
pub(crate) fn as_raw(&self) -> *mut JSPrincipals {
self.0.as_ptr()
}
}
impl Clone for ServoJSPrincipals {
#[inline]
fn clone(&self) -> Self {
unsafe { Self::from_raw_nonnull(self.as_raw_nonnull()) }
}
}
impl Drop for ServoJSPrincipals {
#[inline]
fn drop(&mut self) {
if let Some(cx) = Runtime::get() {
unsafe { JS_DropPrincipals(cx.as_ptr(), self.as_raw()) };
}
}
}
/// A borrowed reference to Servo's `JSPrincipals` instance. Does not update the
/// reference count on creation and deletion.
pub(crate) struct ServoJSPrincipalsRef<'a>(ManuallyDrop<ServoJSPrincipals>, PhantomData<&'a ()>);
impl ServoJSPrincipalsRef<'_> {
/// Construct `Self` from a raw `NonNull<JSPrincipals>`.
///
/// # Safety
///
/// `ServoJSPrincipalsRef` does not update the reference count of the
/// wrapped `JSPrincipals` object. It's up to the caller to ensure the
/// returned `ServoJSPrincipalsRef` object or any clones are not used past
/// the lifetime of the wrapped object.
#[inline]
pub(crate) unsafe fn from_raw_nonnull(raw: NonNull<JSPrincipals>) -> Self {
// Don't use `ServoJSPrincipals::from_raw_nonnull`; we don't want to
// update the reference count
Self(ManuallyDrop::new(ServoJSPrincipals(raw)), PhantomData)
}
/// Construct `Self` from a raw `*mut JSPrincipals`.
///
/// # Safety
///
/// The behavior is undefined if `raw` is null. See also
/// [`Self::from_raw_nonnull`].
#[inline]
pub(crate) unsafe fn from_raw_unchecked(raw: *mut JSPrincipals) -> Self {
Self::from_raw_nonnull(NonNull::new_unchecked(raw))
}
}
impl Clone for ServoJSPrincipalsRef<'_> {
#[inline]
fn clone(&self) -> Self {
Self(ManuallyDrop::new(ServoJSPrincipals(self.0.0)), PhantomData)
}
}
impl Deref for ServoJSPrincipalsRef<'_> {
type Target = ServoJSPrincipals;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
use crate::DomTypeHolder;
#[allow(unused)]
pub(crate) unsafe extern "C" fn destroy_servo_jsprincipal(principals: *mut JSPrincipals) {
@ -175,14 +65,14 @@ pub(crate) unsafe extern "C" fn read_jsprincipal(
let Ok(origin) = bincode::deserialize(&bytes[..]) else {
return false;
};
let principal = ServoJSPrincipals::new(&origin);
let principal = ServoJSPrincipals::new::<DomTypeHolder>(&origin);
*principals = principal.as_raw();
// we transferred ownership of principal to the caller
std::mem::forget(principal);
true
}
const PRINCIPALS_CALLBACKS: JSPrincipalsCallbacks = JSPrincipalsCallbacks {
pub(crate) const PRINCIPALS_CALLBACKS: JSPrincipalsCallbacks = JSPrincipalsCallbacks {
write: Some(write_jsprincipal),
isSystemOrAddonPrincipal: Some(principals_is_system_or_addon_principal),
};

View file

@ -6,198 +6,12 @@
#![deny(missing_docs)]
use std::ffi::CStr;
use std::os::raw::c_char;
use std::ptr;
use js::jsapi::{GetObjectRealmOrNull, GetRealmPrincipals, HandleObject as RawHandleObject};
use js::rust::get_context_realm;
use script_bindings::principals::ServoJSPrincipalsRef;
pub(crate) use script_bindings::proxyhandler::*;
use js::conversions::ToJSValConvertible;
use js::glue::{
GetProxyHandler, GetProxyHandlerFamily, GetProxyPrivate, InvokeGetOwnPropertyDescriptor,
SetProxyPrivate,
};
use js::jsapi;
use js::jsapi::{
DOMProxyShadowsResult, GetObjectRealmOrNull, GetRealmPrincipals, GetStaticPrototype,
GetWellKnownSymbol, Handle as RawHandle, HandleId as RawHandleId,
HandleObject as RawHandleObject, HandleValue as RawHandleValue, JS_AtomizeAndPinString,
JS_DefinePropertyById, JS_GetOwnPropertyDescriptorById, JS_IsExceptionPending, JSAutoRealm,
JSContext, JSErrNum, JSFunctionSpec, JSObject, JSPropertySpec,
MutableHandle as RawMutableHandle, MutableHandleIdVector as RawMutableHandleIdVector,
MutableHandleObject as RawMutableHandleObject, MutableHandleValue as RawMutableHandleValue,
ObjectOpResult, PropertyDescriptor, SetDOMProxyInformation, SymbolCode, jsid,
};
use js::jsid::SymbolId;
use js::jsval::{ObjectValue, UndefinedValue};
use js::rust::wrappers::{
AppendToIdVector, JS_AlreadyHasOwnPropertyById, JS_NewObjectWithGivenProto,
RUST_INTERNED_STRING_TO_JSID, SetDataPropertyDescriptor,
};
use js::rust::{
Handle, HandleObject, HandleValue, MutableHandle, MutableHandleObject, get_context_realm,
};
use crate::dom::bindings::conversions::{is_dom_proxy, jsid_to_string, jsstring_to_str};
use crate::dom::bindings::error::{Error, throw_dom_exception};
use crate::dom::bindings::principals::ServoJSPrincipalsRef;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::utils::delete_property_by_id;
use crate::dom::globalscope::{GlobalScope, GlobalScopeHelpers};
use crate::realms::{AlreadyInRealm, InRealm};
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
/// Determine if this id shadows any existing properties for this proxy.
pub(crate) unsafe extern "C" fn shadow_check_callback(
cx: *mut JSContext,
object: RawHandleObject,
id: RawHandleId,
) -> DOMProxyShadowsResult {
// TODO: support OverrideBuiltins when #12978 is fixed.
rooted!(in(cx) let mut expando = ptr::null_mut::<JSObject>());
get_expando_object(object, expando.handle_mut());
if !expando.get().is_null() {
let mut has_own = false;
let raw_id = Handle::from_raw(id);
if !JS_AlreadyHasOwnPropertyById(cx, expando.handle(), raw_id, &mut has_own) {
return DOMProxyShadowsResult::ShadowCheckFailed;
}
if has_own {
return DOMProxyShadowsResult::ShadowsViaDirectExpando;
}
}
// Our expando, if any, didn't shadow, so we're not shadowing at all.
DOMProxyShadowsResult::DoesntShadow
}
/// Initialize the infrastructure for DOM proxy objects.
pub(crate) unsafe fn init() {
SetDOMProxyInformation(
GetProxyHandlerFamily(),
Some(shadow_check_callback),
ptr::null(),
);
}
/// Defines an expando on the given `proxy`.
pub(crate) unsafe extern "C" fn define_property(
cx: *mut JSContext,
proxy: RawHandleObject,
id: RawHandleId,
desc: RawHandle<PropertyDescriptor>,
result: *mut ObjectOpResult,
) -> bool {
rooted!(in(cx) let mut expando = ptr::null_mut::<JSObject>());
ensure_expando_object(cx, proxy, expando.handle_mut());
JS_DefinePropertyById(cx, expando.handle().into(), id, desc, result)
}
/// Deletes an expando off the given `proxy`.
pub(crate) unsafe extern "C" fn delete(
cx: *mut JSContext,
proxy: RawHandleObject,
id: RawHandleId,
bp: *mut ObjectOpResult,
) -> bool {
rooted!(in(cx) let mut expando = ptr::null_mut::<JSObject>());
get_expando_object(proxy, expando.handle_mut());
if expando.is_null() {
(*bp).code_ = 0 /* OkCode */;
return true;
}
delete_property_by_id(cx, expando.handle(), Handle::from_raw(id), bp)
}
/// Controls whether the Extensible bit can be changed
pub(crate) unsafe extern "C" fn prevent_extensions(
_cx: *mut JSContext,
_proxy: RawHandleObject,
result: *mut ObjectOpResult,
) -> bool {
(*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as ::libc::uintptr_t;
true
}
/// Reports whether the object is Extensible
pub(crate) unsafe extern "C" fn is_extensible(
_cx: *mut JSContext,
_proxy: RawHandleObject,
succeeded: *mut bool,
) -> bool {
*succeeded = true;
true
}
/// If `proxy` (underneath any functionally-transparent wrapper proxies) has as
/// its `[[GetPrototypeOf]]` trap the ordinary `[[GetPrototypeOf]]` behavior
/// defined for ordinary objects, set `*is_ordinary` to true and store `obj`'s
/// prototype in `proto`. Otherwise set `*isOrdinary` to false. In case of
/// error, both outparams have unspecified value.
///
/// This implementation always handles the case of the ordinary
/// `[[GetPrototypeOf]]` behavior. An alternative implementation will be
/// necessary for maybe-cross-origin objects.
pub(crate) unsafe extern "C" fn get_prototype_if_ordinary(
_: *mut JSContext,
proxy: RawHandleObject,
is_ordinary: *mut bool,
proto: RawMutableHandleObject,
) -> bool {
*is_ordinary = true;
proto.set(GetStaticPrototype(proxy.get()));
true
}
/// Get the expando object, or null if there is none.
pub(crate) unsafe fn get_expando_object(obj: RawHandleObject, mut expando: MutableHandleObject) {
assert!(is_dom_proxy(obj.get()));
let val = &mut UndefinedValue();
GetProxyPrivate(obj.get(), val);
expando.set(if val.is_undefined() {
ptr::null_mut()
} else {
val.to_object()
});
}
/// Get the expando object, or create it if it doesn't exist yet.
/// Fails on JSAPI failure.
pub(crate) unsafe fn ensure_expando_object(
cx: *mut JSContext,
obj: RawHandleObject,
mut expando: MutableHandleObject,
) {
assert!(is_dom_proxy(obj.get()));
get_expando_object(obj, expando.reborrow());
if expando.is_null() {
expando.set(JS_NewObjectWithGivenProto(
cx,
ptr::null_mut(),
HandleObject::null(),
));
assert!(!expando.is_null());
SetProxyPrivate(obj.get(), &ObjectValue(expando.get()));
}
}
/// Set the property descriptor's object to `obj` and set it to enumerable,
/// and writable if `readonly` is true.
pub(crate) fn set_property_descriptor(
desc: MutableHandle<PropertyDescriptor>,
value: HandleValue,
attrs: u32,
is_none: &mut bool,
) {
unsafe {
SetDataPropertyDescriptor(desc, value, attrs);
}
*is_none = false;
}
use crate::script_runtime::JSContext as SafeJSContext;
/// <https://html.spec.whatwg.org/multipage/#isplatformobjectsameorigin-(-o-)>
pub(crate) unsafe fn is_platform_object_same_origin(
@ -232,535 +46,3 @@ pub(crate) unsafe fn is_platform_object_same_origin(
result
}
/// Report a cross-origin denial for a property, Always returns `false`, so it
/// can be used as `return report_cross_origin_denial(...);`.
///
/// What this function does corresponds to the operations in
/// <https://html.spec.whatwg.org/multipage/#the-location-interface> denoted as
/// "Throw a `SecurityError` DOMException".
pub(crate) unsafe fn report_cross_origin_denial(
cx: SafeJSContext,
id: RawHandleId,
access: &str,
) -> bool {
debug!(
"permission denied to {} property {} on cross-origin object",
access,
id_to_source(cx, id).as_deref().unwrap_or("< error >"),
);
let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
if !JS_IsExceptionPending(*cx) {
let global = GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof));
// TODO: include `id` and `access` in the exception message
throw_dom_exception(cx, &global, Error::Security, CanGc::note());
}
false
}
unsafe fn id_to_source(cx: SafeJSContext, id: RawHandleId) -> Option<DOMString> {
rooted!(in(*cx) let mut value = UndefinedValue());
rooted!(in(*cx) let mut jsstr = ptr::null_mut::<jsapi::JSString>());
jsapi::JS_IdToValue(*cx, id.get(), value.handle_mut().into())
.then(|| {
jsstr.set(jsapi::JS_ValueToSource(*cx, value.handle().into()));
jsstr.get()
})
.and_then(ptr::NonNull::new)
.map(|jsstr| jsstring_to_str(*cx, jsstr))
}
/// Property and method specs that correspond to the elements of
/// [`CrossOriginProperties(O)`].
///
/// [`CrossOriginProperties(O)`]: https://html.spec.whatwg.org/multipage/#crossoriginproperties-(-o-)
pub(crate) struct CrossOriginProperties {
pub(crate) attributes: &'static [JSPropertySpec],
pub(crate) methods: &'static [JSFunctionSpec],
}
impl CrossOriginProperties {
/// Enumerate the property keys defined by `self`.
fn keys(&self) -> impl Iterator<Item = *const c_char> + '_ {
// Safety: All cross-origin property keys are strings, not symbols
self.attributes
.iter()
.map(|spec| unsafe { spec.name.string_ })
.chain(self.methods.iter().map(|spec| unsafe { spec.name.string_ }))
.filter(|ptr| !ptr.is_null())
}
}
/// Implementation of [`CrossOriginOwnPropertyKeys`].
///
/// [`CrossOriginOwnPropertyKeys`]: https://html.spec.whatwg.org/multipage/#crossoriginownpropertykeys-(-o-)
pub(crate) unsafe fn cross_origin_own_property_keys(
cx: SafeJSContext,
_proxy: RawHandleObject,
cross_origin_properties: &'static CrossOriginProperties,
props: RawMutableHandleIdVector,
) -> bool {
// > 2. For each `e` of `! CrossOriginProperties(O)`, append
// > `e.[[Property]]` to `keys`.
for key in cross_origin_properties.keys() {
rooted!(in(*cx) let rooted = JS_AtomizeAndPinString(*cx, key));
rooted!(in(*cx) let mut rooted_jsid: jsid);
RUST_INTERNED_STRING_TO_JSID(*cx, rooted.handle().get(), rooted_jsid.handle_mut());
AppendToIdVector(props, rooted_jsid.handle());
}
// > 3. Return the concatenation of `keys` and `« "then", @@toStringTag,
// > @@hasInstance, @@isConcatSpreadable »`.
append_cross_origin_allowlisted_prop_keys(cx, props);
true
}
/// Implementation of `[[Set]]` for [`Location`].
///
/// [`Location`]: https://html.spec.whatwg.org/multipage/#location-set
pub(crate) unsafe extern "C" fn maybe_cross_origin_set_rawcx(
cx: *mut JSContext,
proxy: RawHandleObject,
id: RawHandleId,
v: RawHandleValue,
receiver: RawHandleValue,
result: *mut ObjectOpResult,
) -> bool {
let cx = SafeJSContext::from_ptr(cx);
if !is_platform_object_same_origin(cx, proxy) {
return cross_origin_set(cx, proxy, id, v, receiver, result);
}
// Safe to enter the Realm of proxy now.
let _ac = JSAutoRealm::new(*cx, proxy.get());
// OrdinarySet
// <https://tc39.es/ecma262/#sec-ordinaryset>
rooted!(in(*cx) let mut own_desc = PropertyDescriptor::default());
let mut is_none = false;
if !InvokeGetOwnPropertyDescriptor(
GetProxyHandler(*proxy),
*cx,
proxy,
id,
own_desc.handle_mut().into(),
&mut is_none,
) {
return false;
}
js::jsapi::SetPropertyIgnoringNamedGetter(
*cx,
proxy,
id,
v,
receiver,
own_desc.handle().into(),
result,
)
}
pub(crate) unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx(
_: *mut JSContext,
_proxy: RawHandleObject,
is_ordinary: *mut bool,
_proto: RawMutableHandleObject,
) -> bool {
// We have a custom `[[GetPrototypeOf]]`, so return `false`
*is_ordinary = false;
true
}
/// Implementation of `[[GetPrototypeOf]]` for [`Location`].
///
/// [`Location`]: https://html.spec.whatwg.org/multipage/#location-getprototypeof
pub(crate) unsafe fn maybe_cross_origin_get_prototype<D: crate::DomTypes>(
cx: SafeJSContext,
proxy: RawHandleObject,
get_proto_object: unsafe fn(cx: SafeJSContext, global: HandleObject, rval: MutableHandleObject),
proto: RawMutableHandleObject,
) -> bool {
// > 1. If ! IsPlatformObjectSameOrigin(this) is true, then return ! OrdinaryGetPrototypeOf(this).
if is_platform_object_same_origin(cx, proxy) {
let ac = JSAutoRealm::new(*cx, proxy.get());
let global = D::GlobalScope::from_context(*cx, InRealm::Entered(&ac));
get_proto_object(
cx,
global.reflector().get_jsobject(),
MutableHandleObject::from_raw(proto),
);
return !proto.is_null();
}
// > 2. Return null.
proto.set(ptr::null_mut());
true
}
/// Implementation of `[[SetPrototypeOf]]` for [`Location`] and [`WindowProxy`].
///
/// [`Location`]: https://html.spec.whatwg.org/multipage/#location-setprototypeof
/// [`WindowProxy`]: https://html.spec.whatwg.org/multipage/#windowproxy-setprototypeof
pub(crate) unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx(
cx: *mut JSContext,
proxy: RawHandleObject,
proto: RawHandleObject,
result: *mut ObjectOpResult,
) -> bool {
// > 1. Return `! SetImmutablePrototype(this, V)`.
//
// <https://tc39.es/ecma262/#sec-set-immutable-prototype>:
//
// > 1. Assert: Either `Type(V)` is Object or `Type(V)` is Null.
//
// > 2. Let current be `? O.[[GetPrototypeOf]]()`.
rooted!(in(cx) let mut current = ptr::null_mut::<JSObject>());
if !jsapi::GetObjectProto(cx, proxy, current.handle_mut().into()) {
return false;
}
// > 3. If `SameValue(V, current)` is true, return true.
if proto.get() == current.get() {
(*result).code_ = 0 /* OkCode */;
return true;
}
// > 4. Return false.
(*result).code_ = JSErrNum::JSMSG_CANT_SET_PROTO as usize;
true
}
/// Implementation of [`CrossOriginGet`].
///
/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy
/// for a maybe-cross-origin object.
///
/// [`CrossOriginGet`]: https://html.spec.whatwg.org/multipage/#crossoriginget-(-o,-p,-receiver-)
pub(crate) unsafe fn cross_origin_get(
cx: SafeJSContext,
proxy: RawHandleObject,
receiver: RawHandleValue,
id: RawHandleId,
vp: RawMutableHandleValue,
) -> bool {
// > 1. Let `desc` be `? O.[[GetOwnProperty]](P)`.
rooted!(in(*cx) let mut descriptor = PropertyDescriptor::default());
let mut is_none = false;
if !InvokeGetOwnPropertyDescriptor(
GetProxyHandler(*proxy),
*cx,
proxy,
id,
descriptor.handle_mut().into(),
&mut is_none,
) {
return false;
}
// > 2. Assert: `desc` is not undefined.
assert!(
!is_none,
"Callees should throw in all cases when they are not finding \
a property decriptor"
);
// > 3. If `! IsDataDescriptor(desc)` is true, then return `desc.[[Value]]`.
if is_data_descriptor(&descriptor) {
vp.set(descriptor.value_);
return true;
}
// > 4. Assert: `IsAccessorDescriptor(desc)` is `true`.
assert!(is_accessor_descriptor(&descriptor));
// > 5. Let `getter` be `desc.[[Get]]`.
// >
// > 6. If `getter` is `undefined`, then throw a `SecurityError`
// > `DOMException`.
rooted!(in(*cx) let mut getter = ptr::null_mut::<JSObject>());
get_getter_object(&descriptor, getter.handle_mut().into());
if getter.get().is_null() {
return report_cross_origin_denial(cx, id, "get");
}
rooted!(in(*cx) let mut getter_jsval = UndefinedValue());
getter.get().to_jsval(*cx, getter_jsval.handle_mut());
// > 7. Return `? Call(getter, Receiver)`.
jsapi::Call(
*cx,
receiver,
getter_jsval.handle().into(),
&jsapi::HandleValueArray::empty(),
vp,
)
}
/// Implementation of [`CrossOriginSet`].
///
/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy
/// for a maybe-cross-origin object.
///
/// [`CrossOriginSet`]: https://html.spec.whatwg.org/multipage/#crossoriginset-(-o,-p,-v,-receiver-)
pub(crate) unsafe fn cross_origin_set(
cx: SafeJSContext,
proxy: RawHandleObject,
id: RawHandleId,
v: RawHandleValue,
receiver: RawHandleValue,
result: *mut ObjectOpResult,
) -> bool {
// > 1. Let desc be ? O.[[GetOwnProperty]](P).
rooted!(in(*cx) let mut descriptor = PropertyDescriptor::default());
let mut is_none = false;
if !InvokeGetOwnPropertyDescriptor(
GetProxyHandler(*proxy),
*cx,
proxy,
id,
descriptor.handle_mut().into(),
&mut is_none,
) {
return false;
}
// > 2. Assert: desc is not undefined.
assert!(
!is_none,
"Callees should throw in all cases when they are not finding \
a property decriptor"
);
// > 3. If desc.[[Set]] is present and its value is not undefined,
// > then: [...]
rooted!(in(*cx) let mut setter = ptr::null_mut::<JSObject>());
get_setter_object(&descriptor, setter.handle_mut().into());
if setter.get().is_null() {
// > 4. Throw a "SecurityError" DOMException.
return report_cross_origin_denial(cx, id, "set");
}
rooted!(in(*cx) let mut setter_jsval = UndefinedValue());
setter.get().to_jsval(*cx, setter_jsval.handle_mut());
// > 3.1. Perform ? Call(setter, Receiver, «V»).
// >
// > 3.2. Return true.
rooted!(in(*cx) let mut ignored = UndefinedValue());
if !jsapi::Call(
*cx,
receiver,
setter_jsval.handle().into(),
// FIXME: Our binding lacks `HandleValueArray(Handle<Value>)`
// <https://searchfox.org/mozilla-central/rev/072710086ddfe25aa2962c8399fefb2304e8193b/js/public/ValueArray.h#54-55>
&jsapi::HandleValueArray {
length_: 1,
elements_: v.ptr,
},
ignored.handle_mut().into(),
) {
return false;
}
(*result).code_ = 0 /* OkCode */;
true
}
unsafe fn get_getter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
if d.hasGetter_() {
out.set(d.getter_);
}
}
unsafe fn get_setter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
if d.hasSetter_() {
out.set(d.setter_);
}
}
/// <https://tc39.es/ecma262/#sec-isaccessordescriptor>
fn is_accessor_descriptor(d: &PropertyDescriptor) -> bool {
d.hasSetter_() || d.hasGetter_()
}
/// <https://tc39.es/ecma262/#sec-isdatadescriptor>
fn is_data_descriptor(d: &PropertyDescriptor) -> bool {
d.hasWritable_() || d.hasValue_()
}
/// Evaluate `CrossOriginGetOwnPropertyHelper(proxy, id) != null`.
/// SpiderMonkey-specific.
///
/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy
/// for a maybe-cross-origin object.
pub(crate) unsafe fn cross_origin_has_own(
cx: SafeJSContext,
_proxy: RawHandleObject,
cross_origin_properties: &'static CrossOriginProperties,
id: RawHandleId,
bp: *mut bool,
) -> bool {
// TODO: Once we have the slot for the holder, it'd be more efficient to
// use `ensure_cross_origin_property_holder`. We'll need `_proxy` to
// do that.
*bp = jsid_to_string(*cx, Handle::from_raw(id)).is_some_and(|key| {
cross_origin_properties.keys().any(|defined_key| {
let defined_key = CStr::from_ptr(defined_key);
defined_key.to_bytes() == key.as_bytes()
})
});
true
}
/// Implementation of [`CrossOriginGetOwnPropertyHelper`].
///
/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy
/// for a maybe-cross-origin object.
///
/// [`CrossOriginGetOwnPropertyHelper`]: https://html.spec.whatwg.org/multipage/#crossorigingetownpropertyhelper-(-o,-p-)
pub(crate) unsafe fn cross_origin_get_own_property_helper(
cx: SafeJSContext,
proxy: RawHandleObject,
cross_origin_properties: &'static CrossOriginProperties,
id: RawHandleId,
desc: RawMutableHandle<PropertyDescriptor>,
is_none: &mut bool,
) -> bool {
rooted!(in(*cx) let mut holder = ptr::null_mut::<JSObject>());
ensure_cross_origin_property_holder(
cx,
proxy,
cross_origin_properties,
holder.handle_mut().into(),
);
JS_GetOwnPropertyDescriptorById(*cx, holder.handle().into(), id, desc, is_none)
}
/// Implementation of [`CrossOriginPropertyFallback`].
///
/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy
/// for a maybe-cross-origin object.
///
/// [`CrossOriginPropertyFallback`]: https://html.spec.whatwg.org/multipage/#crossoriginpropertyfallback-(-p-)
pub(crate) unsafe fn cross_origin_property_fallback(
cx: SafeJSContext,
_proxy: RawHandleObject,
id: RawHandleId,
desc: RawMutableHandle<PropertyDescriptor>,
is_none: &mut bool,
) -> bool {
assert!(*is_none, "why are we being called?");
// > 1. If P is `then`, `@@toStringTag`, `@@hasInstance`, or
// > `@@isConcatSpreadable`, then return `PropertyDescriptor{ [[Value]]:
// > undefined, [[Writable]]: false, [[Enumerable]]: false,
// > [[Configurable]]: true }`.
if is_cross_origin_allowlisted_prop(cx, id) {
set_property_descriptor(
MutableHandle::from_raw(desc),
HandleValue::undefined(),
jsapi::JSPROP_READONLY as u32,
is_none,
);
return true;
}
// > 2. Throw a `SecurityError` `DOMException`.
report_cross_origin_denial(cx, id, "access")
}
const ALLOWLISTED_SYMBOL_CODES: &[SymbolCode] = &[
SymbolCode::toStringTag,
SymbolCode::hasInstance,
SymbolCode::isConcatSpreadable,
];
unsafe fn is_cross_origin_allowlisted_prop(cx: SafeJSContext, id: RawHandleId) -> bool {
if jsid_to_string(*cx, Handle::from_raw(id)).is_some_and(|st| st == "then") {
return true;
}
rooted!(in(*cx) let mut allowed_id: jsid);
ALLOWLISTED_SYMBOL_CODES.iter().any(|&allowed_code| {
allowed_id.set(SymbolId(GetWellKnownSymbol(*cx, allowed_code)));
// `jsid`s containing `JS::Symbol *` can be compared by
// referential equality
allowed_id.get().asBits_ == id.asBits_
})
}
/// Append `« "then", @@toStringTag, @@hasInstance, @@isConcatSpreadable »` to
/// `props`. This is used to implement [`CrossOriginOwnPropertyKeys`].
///
/// [`CrossOriginOwnPropertyKeys`]: https://html.spec.whatwg.org/multipage/#crossoriginownpropertykeys-(-o-)
unsafe fn append_cross_origin_allowlisted_prop_keys(
cx: SafeJSContext,
props: RawMutableHandleIdVector,
) {
rooted!(in(*cx) let mut id: jsid);
let jsstring = JS_AtomizeAndPinString(*cx, c"then".as_ptr());
rooted!(in(*cx) let rooted = jsstring);
RUST_INTERNED_STRING_TO_JSID(*cx, rooted.handle().get(), id.handle_mut());
AppendToIdVector(props, id.handle());
for &allowed_code in ALLOWLISTED_SYMBOL_CODES.iter() {
id.set(SymbolId(GetWellKnownSymbol(*cx, allowed_code)));
AppendToIdVector(props, id.handle());
}
}
/// Get the holder for cross-origin properties for the current global of the
/// `JSContext`, creating one and storing it in a slot of the proxy object if it
/// doesn't exist yet.
///
/// This essentially creates a cache of [`CrossOriginGetOwnPropertyHelper`]'s
/// results for all property keys.
///
/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy
/// for a maybe-cross-origin object. The `out_holder` return value will always
/// be in the Realm of `cx`.
///
/// [`CrossOriginGetOwnPropertyHelper`]: https://html.spec.whatwg.org/multipage/#crossorigingetownpropertyhelper-(-o,-p-)
unsafe fn ensure_cross_origin_property_holder(
cx: SafeJSContext,
_proxy: RawHandleObject,
cross_origin_properties: &'static CrossOriginProperties,
out_holder: RawMutableHandleObject,
) -> bool {
// TODO: We don't have the slot to store the holder yet. For now,
// the holder is constructed every time this function is called,
// which is not only inefficient but also deviates from the
// specification in a subtle yet observable way.
// Create a holder for the current Realm
out_holder.set(jsapi::JS_NewObjectWithGivenProto(
*cx,
ptr::null_mut(),
RawHandleObject::null(),
));
if out_holder.get().is_null() ||
!jsapi::JS_DefineProperties(
*cx,
out_holder.handle(),
cross_origin_properties.attributes.as_ptr(),
) ||
!jsapi::JS_DefineFunctions(
*cx,
out_holder.handle(),
cross_origin_properties.methods.as_ptr(),
)
{
return false;
}
// TODO: Store the holder in the slot that we don't have yet.
true
}

View file

@ -5,15 +5,14 @@
//! The `Reflector` struct.
use js::rust::HandleObject;
use script_bindings::interfaces::GlobalScopeHelpers;
use crate::DomTypes;
use crate::dom::bindings::conversions::DerivedFrom;
use crate::dom::bindings::iterable::{Iterable, IterableIterator};
use crate::dom::bindings::root::{Dom, DomRoot, Root};
use crate::dom::bindings::trace::JSTraceable;
use crate::dom::globalscope::{GlobalScope, GlobalScopeHelpers};
use crate::realms::{AlreadyInRealm, InRealm};
use crate::script_runtime::{CanGc, JSContext};
use crate::dom::bindings::root::DomRoot;
use crate::dom::globalscope::GlobalScope;
use crate::realms::InRealm;
use crate::script_runtime::CanGc;
/// Create the reflector for a new DOM object and yield ownership to the
/// reflector.
@ -42,31 +41,6 @@ where
unsafe { T::WRAP(D::GlobalScope::get_cx(), global_scope, proto, obj, can_gc) }
}
pub(crate) trait DomGlobalGeneric<D: DomTypes>: DomObject {
/// Returns the [`GlobalScope`] of the realm that the [`DomObject`] was created in. If this
/// object is a `Node`, this will be different from it's owning `Document` if adopted by. For
/// `Node`s it's almost always better to use `NodeTraits::owning_global`.
fn global_(&self, realm: InRealm) -> DomRoot<D::GlobalScope>
where
Self: Sized,
{
D::GlobalScope::from_reflector(self, realm)
}
/// Returns the [`GlobalScope`] of the realm that the [`DomObject`] was created in. If this
/// object is a `Node`, this will be different from it's owning `Document` if adopted by. For
/// `Node`s it's almost always better to use `NodeTraits::owning_global`.
fn global(&self) -> DomRoot<D::GlobalScope>
where
Self: Sized,
{
let realm = AlreadyInRealm::assert_for_cx(D::GlobalScope::get_cx());
D::GlobalScope::from_reflector(self, InRealm::already(&realm))
}
}
impl<D: DomTypes, T: DomObject> DomGlobalGeneric<D> for T {}
pub(crate) trait DomGlobal {
fn global_(&self, realm: InRealm) -> DomRoot<GlobalScope>;
fn global(&self) -> DomRoot<GlobalScope>;
@ -81,35 +55,4 @@ impl<T: DomGlobalGeneric<crate::DomTypeHolder>> DomGlobal for T {
}
}
pub(crate) use script_bindings::reflector::{DomObject, MutDomObject, Reflector};
/// A trait to provide a function pointer to wrap function for DOM objects.
pub(crate) trait DomObjectWrap<D: DomTypes>:
Sized + DomObject + DomGlobalGeneric<D>
{
/// Function pointer to the general wrap function type
#[allow(clippy::type_complexity)]
const WRAP: unsafe fn(
JSContext,
&D::GlobalScope,
Option<HandleObject>,
Box<Self>,
CanGc,
) -> Root<Dom<Self>>;
}
/// A trait to provide a function pointer to wrap function for
/// DOM iterator interfaces.
pub(crate) trait DomObjectIteratorWrap<D: DomTypes>:
DomObjectWrap<D> + JSTraceable + Iterable
{
/// Function pointer to the wrap function for `IterableIterator<T>`
#[allow(clippy::type_complexity)]
const ITER_WRAP: unsafe fn(
JSContext,
&D::GlobalScope,
Option<HandleObject>,
Box<IterableIterator<D, Self>>,
CanGc,
) -> Root<Dom<IterableIterator<D, Self>>>;
}
pub(crate) use script_bindings::reflector::*;

View file

@ -57,26 +57,6 @@ impl Drop for ThreadLocalStackRoots<'_> {
}
}
/// Get a slice of references to DOM objects.
pub(crate) trait DomSlice<T>
where
T: JSTraceable + DomObject,
{
/// Returns the slice of `T` references.
fn r(&self) -> &[&T];
}
impl<T> DomSlice<T> for [Dom<T>]
where
T: JSTraceable + DomObject,
{
#[inline]
fn r(&self) -> &[&T] {
let _ = mem::transmute::<Dom<T>, &T>;
unsafe { &*(self as *const [Dom<T>] as *const [&T]) }
}
}
pub(crate) trait ToLayout<T> {
/// Returns `LayoutDom<T>` containing the same pointer.
///

View file

@ -6,13 +6,12 @@
//! (<https://html.spec.whatwg.org/multipage/#serializable-objects>).
use std::collections::HashMap;
use std::num::NonZeroU32;
use base::id::PipelineNamespaceId;
use base::id::{Index, NamespaceIndex, PipelineNamespaceId};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::structuredclone::{StructuredData, StructuredDataReader};
use crate::dom::bindings::structuredclone::StructuredData;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::CanGc;
@ -25,9 +24,9 @@ pub(crate) struct StorageKey {
}
impl StorageKey {
pub(crate) fn new(name_space: PipelineNamespaceId, index: NonZeroU32) -> StorageKey {
let name_space = name_space.0.to_ne_bytes();
let index = index.get().to_ne_bytes();
pub(crate) fn new<T>(index: NamespaceIndex<T>) -> StorageKey {
let name_space = index.namespace_id.0.to_ne_bytes();
let index = index.index.0.get().to_ne_bytes();
StorageKey {
index: u32::from_ne_bytes(index),
name_space: u32::from_ne_bytes(name_space),
@ -35,11 +34,13 @@ impl StorageKey {
}
}
pub(crate) trait IntoStorageKey
where
Self: Sized,
{
fn into_storage_key(self) -> StorageKey;
impl<T> From<StorageKey> for NamespaceIndex<T> {
fn from(key: StorageKey) -> NamespaceIndex<T> {
NamespaceIndex {
namespace_id: PipelineNamespaceId(key.name_space),
index: Index::new(key.index).expect("Index must not be zero"),
}
}
}
/// Interface for serializable platform objects.
@ -48,11 +49,11 @@ pub(crate) trait Serializable: DomObject
where
Self: Sized,
{
type Id: Copy + Eq + std::hash::Hash + IntoStorageKey + From<StorageKey>;
type Index: Copy + Eq + std::hash::Hash;
type Data;
/// <https://html.spec.whatwg.org/multipage/#serialization-steps>
fn serialize(&self) -> Result<(Self::Id, Self::Data), ()>;
fn serialize(&self) -> Result<(NamespaceIndex<Self::Index>, Self::Data), ()>;
/// <https://html.spec.whatwg.org/multipage/#deserialization-steps>
fn deserialize(
owner: &GlobalScope,
@ -64,11 +65,7 @@ where
/// Returns the field of [StructuredDataReader]/[StructuredDataWriter] that
/// should be used to read/store serialized instances of this type.
fn serialized_storage(data: StructuredData<'_>) -> &mut Option<HashMap<Self::Id, Self::Data>>;
/// Returns the field of [StructuredDataReader] that should be used to store
/// deserialized instances of this type.
fn deserialized_storage(
reader: &mut StructuredDataReader,
) -> &mut Option<HashMap<StorageKey, DomRoot<Self>>>;
fn serialized_storage<'a>(
data: StructuredData<'a, '_>,
) -> &'a mut Option<HashMap<NamespaceIndex<Self::Index>, Self::Data>>;
}

View file

@ -3,36 +3,20 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use std::marker::PhantomData;
use std::thread;
use js::jsapi::{GetScriptedCallerGlobal, HideScriptedCaller, JSTracer, UnhideScriptedCaller};
use js::jsapi::{GetScriptedCallerGlobal, JSTracer};
use js::rust::Runtime;
use script_bindings::settings_stack::*;
use crate::DomTypes;
use crate::dom::bindings::root::{Dom, DomRoot};
//use script_bindings::interfaces::{DomHelpers, GlobalScopeHelpers};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::trace::JSTraceable;
use crate::dom::bindings::utils::DomHelpers;
use crate::dom::globalscope::{GlobalScope, GlobalScopeHelpers};
use crate::script_runtime::CanGc;
use crate::dom::globalscope::GlobalScope;
thread_local!(pub(super) static STACK: RefCell<Vec<StackEntry<crate::DomTypeHolder>>> = const {
RefCell::new(Vec::new())
});
#[derive(Debug, Eq, JSTraceable, PartialEq)]
enum StackEntryKind {
Incumbent,
Entry,
}
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
#[derive(JSTraceable)]
pub(crate) struct StackEntry<D: DomTypes> {
global: Dom<D::GlobalScope>,
kind: StackEntryKind,
}
/// Traces the script settings stack.
pub(crate) unsafe fn trace(tracer: *mut JSTracer) {
STACK.with(|stack| {
@ -46,61 +30,6 @@ pub(crate) fn is_execution_stack_empty() -> bool {
pub(crate) type AutoEntryScript = GenericAutoEntryScript<crate::DomTypeHolder>;
/// RAII struct that pushes and pops entries from the script settings stack.
pub(crate) struct GenericAutoEntryScript<D: DomTypes> {
global: DomRoot<D::GlobalScope>,
#[cfg(feature = "tracing")]
#[allow(dead_code)]
span: tracing::span::EnteredSpan,
}
impl<D: DomTypes> GenericAutoEntryScript<D> {
/// <https://html.spec.whatwg.org/multipage/#prepare-to-run-script>
pub(crate) fn new(global: &D::GlobalScope) -> Self {
let settings_stack = <D as DomHelpers<D>>::settings_stack();
settings_stack.with(|stack| {
trace!("Prepare to run script with {:p}", global);
let mut stack = stack.borrow_mut();
stack.push(StackEntry {
global: Dom::from_ref(global),
kind: StackEntryKind::Entry,
});
Self {
global: DomRoot::from_ref(global),
#[cfg(feature = "tracing")]
span: tracing::info_span!(
"ScriptEvaluate",
servo_profiling = true,
url = global.get_url().to_string(),
)
.entered(),
}
})
}
}
impl<D: DomTypes> Drop for GenericAutoEntryScript<D> {
/// <https://html.spec.whatwg.org/multipage/#clean-up-after-running-script>
fn drop(&mut self) {
let settings_stack = <D as DomHelpers<D>>::settings_stack();
settings_stack.with(|stack| {
let mut stack = stack.borrow_mut();
let entry = stack.pop().unwrap();
assert_eq!(
&*entry.global as *const D::GlobalScope, &*self.global as *const D::GlobalScope,
"Dropped AutoEntryScript out of order."
);
assert_eq!(entry.kind, StackEntryKind::Entry);
trace!("Clean up after running script with {:p}", &*entry.global);
});
// Step 5
if !thread::panicking() && incumbent_global().is_none() {
self.global.perform_a_microtask_checkpoint(CanGc::note());
}
}
}
/// Returns the ["entry"] global object.
///
/// ["entry"]: https://html.spec.whatwg.org/multipage/#entry
@ -117,67 +46,7 @@ pub(crate) fn entry_global() -> DomRoot<GlobalScope> {
.unwrap()
}
/// RAII struct that pushes and pops entries from the script settings stack.
pub(crate) struct GenericAutoIncumbentScript<D: DomTypes> {
global: usize,
_marker: PhantomData<D>,
}
pub(crate) type AutoIncumbentScript = GenericAutoIncumbentScript<crate::DomTypeHolder>;
impl<D: DomTypes> GenericAutoIncumbentScript<D> {
/// <https://html.spec.whatwg.org/multipage/#prepare-to-run-a-callback>
pub(crate) fn new(global: &D::GlobalScope) -> Self {
// Step 2-3.
unsafe {
let cx =
Runtime::get().expect("Creating a new incumbent script after runtime shutdown");
HideScriptedCaller(cx.as_ptr());
}
let settings_stack = <D as DomHelpers<D>>::settings_stack();
settings_stack.with(|stack| {
trace!("Prepare to run a callback with {:p}", global);
// Step 1.
let mut stack = stack.borrow_mut();
stack.push(StackEntry {
global: Dom::from_ref(global),
kind: StackEntryKind::Incumbent,
});
Self {
global: global as *const _ as usize,
_marker: PhantomData,
}
})
}
}
impl<D: DomTypes> Drop for GenericAutoIncumbentScript<D> {
/// <https://html.spec.whatwg.org/multipage/#clean-up-after-running-a-callback>
fn drop(&mut self) {
let settings_stack = <D as DomHelpers<D>>::settings_stack();
settings_stack.with(|stack| {
// Step 4.
let mut stack = stack.borrow_mut();
let entry = stack.pop().unwrap();
// Step 3.
assert_eq!(
&*entry.global as *const D::GlobalScope as usize, self.global,
"Dropped AutoIncumbentScript out of order."
);
assert_eq!(entry.kind, StackEntryKind::Incumbent);
trace!(
"Clean up after running a callback with {:p}",
&*entry.global
);
});
unsafe {
// Step 1-2.
if let Some(cx) = Runtime::get() {
UnhideScriptedCaller(cx.as_ptr());
}
}
}
}
pub type AutoIncumbentScript = GenericAutoIncumbentScript<crate::DomTypeHolder>;
/// Returns the ["incumbent"] global object.
///

View file

@ -5,18 +5,25 @@
//! This module implements structured cloning, as defined by [HTML](https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data).
use std::collections::HashMap;
use std::num::NonZeroU32;
use std::ffi::CStr;
use std::os::raw;
use std::ptr;
use base::id::{BlobId, DomPointId, MessagePortId, PipelineNamespaceId};
use base::id::{
BlobId, DomExceptionId, DomPointId, Index, MessagePortId, NamespaceIndex, PipelineNamespaceId,
};
use constellation_traits::{
BlobImpl, DomException, DomPoint, MessagePortImpl, Serializable as SerializableInterface,
StructuredSerializedData, Transferrable as TransferrableInterface,
};
use js::gc::RootedVec;
use js::glue::{
CopyJSStructuredCloneData, DeleteJSAutoStructuredCloneBuffer, GetLengthOfJSStructuredCloneData,
NewJSAutoStructuredCloneBuffer, WriteBytesToJSStructuredCloneData,
};
use js::jsapi::{
CloneDataPolicy, HandleObject as RawHandleObject, JS_ClearPendingException, JS_ReadUint32Pair,
JS_STRUCTURED_CLONE_VERSION, JS_WriteUint32Pair, JSContext, JSObject,
CloneDataPolicy, HandleObject as RawHandleObject, Heap, JS_ClearPendingException,
JS_ReadUint32Pair, JS_STRUCTURED_CLONE_VERSION, JS_WriteUint32Pair, JSContext, JSObject,
JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter,
MutableHandleObject as RawMutableHandleObject, StructuredCloneScope, TransferableOwnership,
};
@ -24,24 +31,21 @@ use js::jsval::UndefinedValue;
use js::rust::wrappers::{JS_ReadStructuredClone, JS_WriteStructuredClone};
use js::rust::{CustomAutoRooterGuard, HandleValue, MutableHandleValue};
use script_bindings::conversions::IDLInterface;
use script_traits::serializable::{BlobImpl, DomPoint};
use script_traits::transferable::MessagePortImpl;
use script_traits::{
Serializable as SerializableInterface, StructuredSerializedData,
Transferrable as TransferrableInterface,
};
use strum::IntoEnumIterator;
use crate::dom::bindings::conversions::{ToJSValConvertible, root_from_object};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::serializable::{IntoStorageKey, Serializable, StorageKey};
use crate::dom::bindings::transferable::{ExtractComponents, IdFromComponents, Transferable};
use crate::dom::bindings::serializable::{Serializable, StorageKey};
use crate::dom::bindings::transferable::Transferable;
use crate::dom::blob::Blob;
use crate::dom::dompoint::DOMPoint;
use crate::dom::dompointreadonly::DOMPointReadOnly;
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageport::MessagePort;
use crate::dom::readablestream::ReadableStream;
use crate::dom::types::{DOMException, TransformStream};
use crate::dom::writablestream::WritableStream;
use crate::realms::{AlreadyInRealm, InRealm, enter_realm};
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
@ -58,6 +62,10 @@ pub(super) enum StructuredCloneTags {
Principals = 0xFFFF8003,
DomPointReadOnly = 0xFFFF8004,
DomPoint = 0xFFFF8005,
ReadableStream = 0xFFFF8006,
DomException = 0xFFFF8007,
WritableStream = 0xFFFF8008,
TransformStream = 0xFFFF8009,
Max = 0xFFFFFFFF,
}
@ -67,6 +75,7 @@ impl From<SerializableInterface> for StructuredCloneTags {
SerializableInterface::Blob => StructuredCloneTags::DomBlob,
SerializableInterface::DomPointReadOnly => StructuredCloneTags::DomPointReadOnly,
SerializableInterface::DomPoint => StructuredCloneTags::DomPoint,
SerializableInterface::DomException => StructuredCloneTags::DomException,
}
}
}
@ -75,6 +84,9 @@ impl From<TransferrableInterface> for StructuredCloneTags {
fn from(v: TransferrableInterface) -> Self {
match v {
TransferrableInterface::MessagePort => StructuredCloneTags::MessagePort,
TransferrableInterface::ReadableStream => StructuredCloneTags::ReadableStream,
TransferrableInterface::WritableStream => StructuredCloneTags::WritableStream,
TransferrableInterface::TransformStream => StructuredCloneTags::TransformStream,
}
}
}
@ -84,20 +96,21 @@ fn reader_for_type(
) -> unsafe fn(
&GlobalScope,
*mut JSStructuredCloneReader,
&mut StructuredDataReader,
&mut StructuredDataReader<'_>,
CanGc,
) -> *mut JSObject {
match val {
SerializableInterface::Blob => read_object::<Blob>,
SerializableInterface::DomPointReadOnly => read_object::<DOMPointReadOnly>,
SerializableInterface::DomPoint => read_object::<DOMPoint>,
SerializableInterface::DomException => read_object::<DOMException>,
}
}
unsafe fn read_object<T: Serializable>(
owner: &GlobalScope,
r: *mut JSStructuredCloneReader,
sc_reader: &mut StructuredDataReader,
sc_reader: &mut StructuredDataReader<'_>,
can_gc: CanGc,
) -> *mut JSObject {
let mut name_space: u32 = 0;
@ -111,7 +124,7 @@ unsafe fn read_object<T: Serializable>(
// 1. Re-build the key for the storage location
// of the serialized object.
let id = T::Id::from(storage_key);
let id: NamespaceIndex<T::Index> = storage_key.into();
// 2. Get the transferred object from its storage, using the key.
let objects = T::serialized_storage(StructuredData::Reader(sc_reader));
@ -126,9 +139,8 @@ unsafe fn read_object<T: Serializable>(
}
if let Ok(obj) = T::deserialize(owner, serialized, can_gc) {
let destination = T::deserialized_storage(sc_reader).get_or_insert_with(HashMap::new);
let reflector = obj.reflector().get_jsobject().get();
destination.insert(storage_key, obj);
sc_reader.roots.push(Heap::boxed(reflector));
return reflector;
}
warn!("Reading structured data failed in {:?}.", owner.get_url());
@ -146,7 +158,7 @@ unsafe fn write_object<T: Serializable>(
let objects = T::serialized_storage(StructuredData::Writer(sc_writer))
.get_or_insert_with(HashMap::new);
objects.insert(new_id, serialized);
let storage_key = new_id.into_storage_key();
let storage_key = StorageKey::new(new_id);
assert!(JS_WriteUint32Pair(
w,
@ -181,7 +193,7 @@ unsafe extern "C" fn read_callback(
"tag should be higher than StructuredCloneTags::Min"
);
let sc_reader = &mut *(closure as *mut StructuredDataReader);
let sc_reader = &mut *(closure as *mut StructuredDataReader<'_>);
let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
let global = GlobalScope::from_context(cx, InRealm::Already(&in_realm_proof));
for serializable in SerializableInterface::iter() {
@ -224,6 +236,7 @@ fn serialize_for_type(val: SerializableInterface) -> SerializeOperation {
SerializableInterface::Blob => try_serialize::<Blob>,
SerializableInterface::DomPointReadOnly => try_serialize::<DOMPointReadOnly>,
SerializableInterface::DomPoint => try_serialize::<DOMPoint>,
SerializableInterface::DomException => try_serialize::<DOMException>,
}
}
@ -248,15 +261,19 @@ unsafe extern "C" fn write_callback(
fn receiver_for_type(
val: TransferrableInterface,
) -> fn(&GlobalScope, &mut StructuredDataReader, u64, RawMutableHandleObject) -> Result<(), ()> {
) -> fn(&GlobalScope, &mut StructuredDataReader<'_>, u64, RawMutableHandleObject) -> Result<(), ()>
{
match val {
TransferrableInterface::MessagePort => receive_object::<MessagePort>,
TransferrableInterface::ReadableStream => receive_object::<ReadableStream>,
TransferrableInterface::WritableStream => receive_object::<WritableStream>,
TransferrableInterface::TransformStream => receive_object::<TransformStream>,
}
}
fn receive_object<T: Transferable>(
owner: &GlobalScope,
sc_reader: &mut StructuredDataReader,
sc_reader: &mut StructuredDataReader<'_>,
extra_data: u64,
return_object: RawMutableHandleObject,
) -> Result<(), ()> {
@ -270,13 +287,13 @@ fn receive_object<T: Transferable>(
.try_into()
.expect("name_space to be a slice of four."),
));
let id = <T::Id as IdFromComponents>::from(
let id: NamespaceIndex<T::Index> = NamespaceIndex {
namespace_id,
NonZeroU32::new(u32::from_ne_bytes(
index: Index::new(u32::from_ne_bytes(
index.try_into().expect("index to be a slice of four."),
))
.expect("Index to be non-zero"),
);
};
// 2. Get the transferred object from its storage, using the key.
let storage = T::serialized_storage(StructuredData::Reader(sc_reader));
@ -292,13 +309,12 @@ fn receive_object<T: Transferable>(
);
};
if let Ok(received) = T::transfer_receive(owner, id, serialized) {
return_object.set(received.reflector().rootable().get());
let storage = T::deserialized_storage(sc_reader).get_or_insert_with(Vec::new);
storage.push(received);
return Ok(());
}
Err(())
let Ok(received) = T::transfer_receive(owner, id, serialized) else {
return Err(());
};
return_object.set(received.reflector().rootable().get());
sc_reader.roots.push(Heap::boxed(return_object.get()));
Ok(())
}
unsafe extern "C" fn read_transfer_callback(
@ -311,9 +327,10 @@ unsafe extern "C" fn read_transfer_callback(
closure: *mut raw::c_void,
return_object: RawMutableHandleObject,
) -> bool {
let sc_reader = &mut *(closure as *mut StructuredDataReader);
let sc_reader = &mut *(closure as *mut StructuredDataReader<'_>);
let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
let owner = GlobalScope::from_context(cx, InRealm::Already(&in_realm_proof));
for transferrable in TransferrableInterface::iter() {
if tag == StructuredCloneTags::from(transferrable) as u32 {
let transfer_receiver = receiver_for_type(transferrable);
@ -343,11 +360,10 @@ unsafe fn try_transfer<T: Transferable + IDLInterface>(
.get_or_insert_with(HashMap::new);
objects.insert(id, object);
let (PipelineNamespaceId(name_space), index) = id.components();
let index = index.get();
let index = id.index.0.get();
let mut big: [u8; 8] = [0; 8];
let name_space = name_space.to_ne_bytes();
let name_space = id.namespace_id.0.to_ne_bytes();
let index = index.to_ne_bytes();
let (left, right) = big.split_at_mut(4);
@ -375,6 +391,9 @@ type TransferOperation = unsafe fn(
fn transfer_for_type(val: TransferrableInterface) -> TransferOperation {
match val {
TransferrableInterface::MessagePort => try_transfer::<MessagePort>,
TransferrableInterface::ReadableStream => try_transfer::<ReadableStream>,
TransferrableInterface::WritableStream => try_transfer::<WritableStream>,
TransferrableInterface::TransformStream => try_transfer::<TransformStream>,
}
}
@ -421,6 +440,9 @@ unsafe fn can_transfer_for_type(
}
match transferable {
TransferrableInterface::MessagePort => can_transfer::<MessagePort>(obj, cx),
TransferrableInterface::ReadableStream => can_transfer::<ReadableStream>(obj, cx),
TransferrableInterface::WritableStream => can_transfer::<WritableStream>(obj, cx),
TransferrableInterface::TransformStream => can_transfer::<TransformStream>(obj, cx),
}
}
@ -441,9 +463,16 @@ unsafe extern "C" fn can_transfer_callback(
unsafe extern "C" fn report_error_callback(
_cx: *mut JSContext,
_errorid: u32,
_closure: *mut ::std::os::raw::c_void,
_error_message: *const ::std::os::raw::c_char,
closure: *mut raw::c_void,
error_message: *const ::std::os::raw::c_char,
) {
let msg_result = unsafe { CStr::from_ptr(error_message).to_str().map(str::to_string) };
if let Ok(msg) = msg_result {
let dom_error_record = &mut *(closure as *mut DOMErrorRecord);
dom_error_record.message = Some(msg)
}
}
unsafe extern "C" fn sab_cloned_callback(
@ -465,22 +494,25 @@ static STRUCTURED_CLONE_CALLBACKS: JSStructuredCloneCallbacks = JSStructuredClon
sabCloned: Some(sab_cloned_callback),
};
pub(crate) enum StructuredData<'a> {
Reader(&'a mut StructuredDataReader),
pub(crate) enum StructuredData<'a, 'b> {
Reader(&'a mut StructuredDataReader<'b>),
Writer(&'a mut StructuredDataWriter),
}
#[derive(Default)]
#[repr(C)]
pub(crate) struct DOMErrorRecord {
pub(crate) message: Option<String>,
}
/// Reader and writer structs for results from, and inputs to, structured-data read/write operations.
/// <https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data>
pub(crate) struct StructuredDataReader {
/// A map of deserialized blobs, stored temporarily here to keep them rooted.
pub(crate) blobs: Option<HashMap<StorageKey, DomRoot<Blob>>>,
/// A map of deserialized points, stored temporarily here to keep them rooted.
pub(crate) points_read_only: Option<HashMap<StorageKey, DomRoot<DOMPointReadOnly>>>,
pub(crate) dom_points: Option<HashMap<StorageKey, DomRoot<DOMPoint>>>,
/// A vec of transfer-received DOM ports,
/// to be made available to script through a message event.
pub(crate) message_ports: Option<Vec<DomRoot<MessagePort>>>,
#[repr(C)]
pub(crate) struct StructuredDataReader<'a> {
/// A struct of error message.
errors: DOMErrorRecord,
/// Rooted copies of every deserialized object to ensure they are not garbage collected.
roots: RootedVec<'a, Box<Heap<*mut JSObject>>>,
/// A map of port implementations,
/// used as part of the "transfer-receiving" steps of ports,
/// to produce the DOM ports stored in `message_ports` above.
@ -491,15 +523,22 @@ pub(crate) struct StructuredDataReader {
pub(crate) blob_impls: Option<HashMap<BlobId, BlobImpl>>,
/// A map of serialized points.
pub(crate) points: Option<HashMap<DomPointId, DomPoint>>,
/// A map of serialized exceptions.
pub(crate) exceptions: Option<HashMap<DomExceptionId, DomException>>,
}
/// A data holder for transferred and serialized objects.
#[derive(Default)]
#[repr(C)]
pub(crate) struct StructuredDataWriter {
/// Error message.
pub(crate) errors: DOMErrorRecord,
/// Transferred ports.
pub(crate) ports: Option<HashMap<MessagePortId, MessagePortImpl>>,
/// Serialized points.
pub(crate) points: Option<HashMap<DomPointId, DomPoint>>,
/// Serialized exceptions.
pub(crate) exceptions: Option<HashMap<DomExceptionId, DomException>>,
/// Serialized blobs.
pub(crate) blobs: Option<HashMap<BlobId, BlobImpl>>,
}
@ -539,7 +578,7 @@ pub(crate) fn write(
);
if !result {
JS_ClearPendingException(*cx);
return Err(Error::DataClone);
return Err(Error::DataClone(sc_writer.errors.message));
}
let nbytes = GetLengthOfJSStructuredCloneData(scdata);
@ -553,6 +592,7 @@ pub(crate) fn write(
serialized: data,
ports: sc_writer.ports.take(),
points: sc_writer.points.take(),
exceptions: sc_writer.exceptions.take(),
blobs: sc_writer.blobs.take(),
};
@ -566,17 +606,17 @@ pub(crate) fn read(
global: &GlobalScope,
mut data: StructuredSerializedData,
rval: MutableHandleValue,
) -> Result<Vec<DomRoot<MessagePort>>, ()> {
) -> Fallible<Vec<DomRoot<MessagePort>>> {
let cx = GlobalScope::get_cx();
let _ac = enter_realm(global);
rooted_vec!(let mut roots);
let mut sc_reader = StructuredDataReader {
blobs: None,
message_ports: None,
points_read_only: None,
dom_points: None,
roots,
port_impls: data.ports.take(),
blob_impls: data.blobs.take(),
points: data.points.take(),
exceptions: data.exceptions.take(),
errors: DOMErrorRecord { message: None },
};
let sc_reader_ptr = &mut sc_reader as *mut _;
unsafe {
@ -605,18 +645,22 @@ pub(crate) fn read(
&STRUCTURED_CLONE_CALLBACKS,
sc_reader_ptr as *mut raw::c_void,
);
if !result {
JS_ClearPendingException(*cx);
return Err(Error::DataClone(sc_reader.errors.message));
}
DeleteJSAutoStructuredCloneBuffer(scbuf);
if result {
// Any transfer-received port-impls should have been taken out.
assert!(sc_reader.port_impls.is_none());
match sc_reader.message_ports.take() {
Some(ports) => return Ok(ports),
None => return Ok(Vec::with_capacity(0)),
}
let mut message_ports = vec![];
for reflector in sc_reader.roots.iter() {
let Ok(message_port) = root_from_object::<MessagePort>(reflector.get(), *cx) else {
continue;
};
message_ports.push(message_port);
}
Err(())
// Any transfer-received port-impls should have been taken out.
assert!(sc_reader.port_impls.is_none());
Ok(message_ports)
}
}

View file

@ -33,16 +33,12 @@ use std::collections::HashMap;
use std::collections::hash_map::RandomState;
use std::fmt::Display;
use std::hash::{BuildHasher, Hash};
use std::mem;
use std::ops::{Deref, DerefMut};
/// A trait to allow tracing (only) DOM objects.
pub(crate) use js::gc::Traceable as JSTraceable;
pub(crate) use js::gc::{RootableVec, RootedVec};
use js::glue::{CallScriptTracer, CallStringTracer, CallValueTracer};
use js::jsapi::{GCTraceKindToAscii, Heap, JSScript, JSString, JSTracer, TraceKind};
use js::jsval::JSVal;
use js::rust::{GCMethods, Handle};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
pub(crate) use script_bindings::trace::*;
@ -268,70 +264,3 @@ unsafe impl<T: DomObject> JSTraceable for Trusted<T> {
// Do nothing
}
}
/// Roots any JSTraceable thing
///
/// If you have a valid DomObject, use DomRoot.
/// If you have GC things like *mut JSObject or JSVal, use rooted!.
/// If you have an arbitrary number of DomObjects to root, use rooted_vec!.
/// If you know what you're doing, use this.
#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
pub(crate) struct RootedTraceableBox<T: JSTraceable + 'static>(js::gc::RootedTraceableBox<T>);
unsafe impl<T: JSTraceable + 'static> JSTraceable for RootedTraceableBox<T> {
unsafe fn trace(&self, tracer: *mut JSTracer) {
self.0.trace(tracer);
}
}
impl<T: JSTraceable + 'static> RootedTraceableBox<T> {
/// DomRoot a JSTraceable thing for the life of this RootedTraceableBox
pub(crate) fn new(traceable: T) -> RootedTraceableBox<T> {
Self(js::gc::RootedTraceableBox::new(traceable))
}
/// Consumes a boxed JSTraceable and roots it for the life of this RootedTraceableBox.
pub(crate) fn from_box(boxed_traceable: Box<T>) -> RootedTraceableBox<T> {
Self(js::gc::RootedTraceableBox::from_box(boxed_traceable))
}
}
impl<T> RootedTraceableBox<Heap<T>>
where
Heap<T>: JSTraceable + 'static,
T: GCMethods + Copy,
{
pub(crate) fn handle(&self) -> Handle<T> {
self.0.handle()
}
}
impl<T: JSTraceable + MallocSizeOf> MallocSizeOf for RootedTraceableBox<T> {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
// Briefly resurrect the real Box value so we can rely on the existing calculations.
// Then immediately forget about it again to avoid dropping the box.
let inner = unsafe { Box::from_raw(self.0.ptr()) };
let size = inner.size_of(ops);
mem::forget(inner);
size
}
}
impl<T: JSTraceable + Default> Default for RootedTraceableBox<T> {
fn default() -> RootedTraceableBox<T> {
RootedTraceableBox::new(T::default())
}
}
impl<T: JSTraceable> Deref for RootedTraceableBox<T> {
type Target = T;
fn deref(&self) -> &T {
self.0.deref()
}
}
impl<T: JSTraceable> DerefMut for RootedTraceableBox<T> {
fn deref_mut(&mut self) -> &mut T {
self.0.deref_mut()
}
}

View file

@ -6,44 +6,33 @@
//! (<https://html.spec.whatwg.org/multipage/#transferable-objects>).
use std::collections::HashMap;
use std::num::NonZeroU32;
use std::hash::Hash;
use base::id::PipelineNamespaceId;
use base::id::NamespaceIndex;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::structuredclone::{StructuredData, StructuredDataReader};
use crate::dom::bindings::structuredclone::StructuredData;
use crate::dom::globalscope::GlobalScope;
pub(crate) trait IdFromComponents
where
Self: Sized,
{
fn from(namespace_id: PipelineNamespaceId, index: NonZeroU32) -> Self;
}
pub(crate) trait ExtractComponents {
fn components(&self) -> (PipelineNamespaceId, NonZeroU32);
}
pub(crate) trait Transferable: DomObject
where
Self: Sized,
{
type Id: Eq + std::hash::Hash + Copy + IdFromComponents + ExtractComponents;
type Index: Copy + Eq + Hash;
type Data;
fn can_transfer(&self) -> bool {
true
}
fn transfer(&self) -> Result<(Self::Id, Self::Data), ()>;
fn transfer(&self) -> Result<(NamespaceIndex<Self::Index>, Self::Data), ()>;
fn transfer_receive(
owner: &GlobalScope,
id: Self::Id,
id: NamespaceIndex<Self::Index>,
serialized: Self::Data,
) -> Result<DomRoot<Self>, ()>;
fn serialized_storage(data: StructuredData<'_>) -> &mut Option<HashMap<Self::Id, Self::Data>>;
fn deserialized_storage(reader: &mut StructuredDataReader) -> &mut Option<Vec<DomRoot<Self>>>;
fn serialized_storage<'a>(
data: StructuredData<'a, '_>,
) -> &'a mut Option<HashMap<NamespaceIndex<Self::Index>, Self::Data>>;
}

View file

@ -5,86 +5,34 @@
//! Various utilities to glue JavaScript and the DOM implementation together.
use std::cell::RefCell;
use std::ffi::CString;
use std::os::raw::c_char;
use std::ptr::NonNull;
use std::sync::OnceLock;
use std::thread::LocalKey;
use std::{ptr, slice, str};
use js::JS_CALLEE;
use js::conversions::ToJSValConvertible;
use js::glue::{
CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper, JS_GetReservedSlot,
RUST_FUNCTION_VALUE_TO_JITINFO, UnwrapObjectDynamic, UnwrapObjectStatic,
};
use js::glue::{IsWrapper, JSPrincipalsCallbacks, UnwrapObjectDynamic, UnwrapObjectStatic};
use js::jsapi::{
AtomToLinearString, CallArgs, DOMCallbacks, ExceptionStackBehavior, GetLinearStringCharAt,
GetLinearStringLength, GetNonCCWObjectGlobal, HandleId as RawHandleId,
HandleObject as RawHandleObject, Heap, JS_ClearPendingException,
JS_DeprecatedStringHasLatin1Chars, JS_EnumerateStandardClasses, JS_FreezeObject,
JS_GetLatin1StringCharsAndLength, JS_IsExceptionPending, JS_IsGlobalObject,
JS_ResolveStandardClass, JSAtom, JSContext, JSJitInfo, JSObject, JSTracer,
MutableHandleIdVector as RawMutableHandleIdVector, MutableHandleValue as RawMutableHandleValue,
ObjectOpResult, StringIsArrayIndex,
};
use js::jsval::{JSVal, UndefinedValue};
use js::rust::wrappers::{
CallOriginalPromiseReject, JS_DeletePropertyById, JS_ForwardGetPropertyTo,
JS_GetPendingException, JS_GetProperty, JS_GetPrototype, JS_HasProperty, JS_HasPropertyById,
JS_SetPendingException, JS_SetProperty,
};
use js::rust::{
GCMethods, Handle, HandleId, HandleObject, HandleValue, MutableHandleValue, ToString,
get_object_class, is_dom_class,
CallArgs, DOMCallbacks, HandleObject as RawHandleObject, JS_FreezeObject, JSContext, JSObject,
};
use js::rust::{HandleObject, MutableHandleValue, get_object_class, is_dom_class};
use script_bindings::interfaces::{DomHelpers, Interface};
use script_bindings::settings_stack::StackEntry;
use crate::DomTypes;
use crate::dom::bindings::codegen::InterfaceObjectMap;
use crate::dom::bindings::codegen::PrototypeList::{self, PROTO_OR_IFACE_LENGTH};
use crate::dom::bindings::constructor::call_html_constructor;
use crate::dom::bindings::conversions::{
DerivedFrom, PrototypeCheck, jsstring_to_str, private_from_proto_check,
use crate::dom::bindings::codegen::{InterfaceObjectMap, PrototypeList};
use crate::dom::bindings::constructor::{
call_html_constructor, pop_current_element_queue, push_new_element_queue,
};
use crate::dom::bindings::error::{Error, throw_dom_exception, throw_invalid_this};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::settings_stack::{self, StackEntry};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::trace::trace_object;
use crate::dom::bindings::conversions::DerivedFrom;
use crate::dom::bindings::error::{Error, report_pending_exception, throw_dom_exception};
use crate::dom::bindings::principals::PRINCIPALS_CALLBACKS;
use crate::dom::bindings::proxyhandler::is_platform_object_same_origin;
use crate::dom::bindings::reflector::{DomObject, DomObjectWrap, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::settings_stack;
use crate::dom::globalscope::GlobalScope;
use crate::dom::windowproxy::WindowProxyHandler;
use crate::realms::InRealm;
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
/// A OnceLock wrapping a type that is not considered threadsafe by the Rust compiler, but
/// will be used in a threadsafe manner (it will not be mutated, after being initialized).
///
/// This is needed to allow using JS API types (which usually involve raw pointers) in static initializers,
/// when Servo guarantees through the use of OnceLock that only one thread will ever initialize
/// the value.
pub(crate) struct ThreadUnsafeOnceLock<T>(OnceLock<T>);
impl<T> ThreadUnsafeOnceLock<T> {
pub(crate) const fn new() -> Self {
Self(OnceLock::new())
}
/// Initialize the value inside this lock. Panics if the lock has been previously initialized.
pub(crate) fn set(&self, val: T) {
assert!(self.0.set(val).is_ok());
}
/// Get a reference to the value inside this lock. Panics if the lock has not been initialized.
///
/// SAFETY:
/// The caller must ensure that it does not mutate value contained inside this lock
/// (using interior mutability).
pub(crate) unsafe fn get(&self) -> &T {
self.0.get().unwrap()
}
}
unsafe impl<T> Sync for ThreadUnsafeOnceLock<T> {}
unsafe impl<T> Send for ThreadUnsafeOnceLock<T> {}
#[derive(JSTraceable, MallocSizeOf)]
/// Static data associated with a global object.
pub(crate) struct GlobalStaticData {
@ -102,21 +50,7 @@ impl GlobalStaticData {
}
}
/// The index of the slot where the object holder of that interface's
/// unforgeable members are defined.
pub(crate) const DOM_PROTO_UNFORGEABLE_HOLDER_SLOT: u32 = 0;
/// The index of the slot that contains a reference to the ProtoOrIfaceArray.
// All DOM globals must have a slot at DOM_PROTOTYPE_SLOT.
pub(crate) const DOM_PROTOTYPE_SLOT: u32 = js::JSCLASS_GLOBAL_SLOT_COUNT;
/// The flag set on the `JSClass`es for DOM global objects.
// NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and
// LSetDOMProperty. Those constants need to be changed accordingly if this value
// changes.
pub(crate) const JSCLASS_DOM_GLOBAL: u32 = js::JSCLASS_USERBIT1;
pub(crate) use script_bindings::utils::{DOMClass, DOMJSClass};
pub(crate) use script_bindings::utils::*;
/// Returns a JSVal representing the frozen JavaScript array
pub(crate) fn to_frozen_array<T: ToJSValConvertible>(
@ -131,132 +65,6 @@ pub(crate) fn to_frozen_array<T: ToJSValConvertible>(
unsafe { JS_FreezeObject(*cx, RawHandleObject::from(obj.handle())) };
}
/// Returns the ProtoOrIfaceArray for the given global object.
/// Fails if `global` is not a DOM global object.
pub(crate) fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray {
unsafe {
assert_ne!(((*get_object_class(global)).flags & JSCLASS_DOM_GLOBAL), 0);
let mut slot = UndefinedValue();
JS_GetReservedSlot(global, DOM_PROTOTYPE_SLOT, &mut slot);
slot.to_private() as *mut ProtoOrIfaceArray
}
}
/// An array of *mut JSObject of size PROTO_OR_IFACE_LENGTH.
pub(crate) type ProtoOrIfaceArray = [*mut JSObject; PROTO_OR_IFACE_LENGTH];
/// Gets the property `id` on `proxy`'s prototype. If it exists, `*found` is
/// set to true and `*vp` to the value, otherwise `*found` is set to false.
///
/// Returns false on JSAPI failure.
pub(crate) unsafe fn get_property_on_prototype(
cx: *mut JSContext,
proxy: HandleObject,
receiver: HandleValue,
id: HandleId,
found: *mut bool,
vp: MutableHandleValue,
) -> bool {
rooted!(in(cx) let mut proto = ptr::null_mut::<JSObject>());
if !JS_GetPrototype(cx, proxy, proto.handle_mut()) || proto.is_null() {
*found = false;
return true;
}
let mut has_property = false;
if !JS_HasPropertyById(cx, proto.handle(), id, &mut has_property) {
return false;
}
*found = has_property;
if !has_property {
return true;
}
JS_ForwardGetPropertyTo(cx, proto.handle(), id, receiver, vp)
}
/// Get an array index from the given `jsid`. Returns `None` if the given
/// `jsid` is not an integer.
pub(crate) unsafe fn get_array_index_from_id(_cx: *mut JSContext, id: HandleId) -> Option<u32> {
let raw_id = *id;
if raw_id.is_int() {
return Some(raw_id.to_int() as u32);
}
if raw_id.is_void() || !raw_id.is_string() {
return None;
}
let atom = raw_id.to_string() as *mut JSAtom;
let s = AtomToLinearString(atom);
if GetLinearStringLength(s) == 0 {
return None;
}
let chars = [GetLinearStringCharAt(s, 0)];
let first_char = char::decode_utf16(chars.iter().cloned())
.next()
.map_or('\0', |r| r.unwrap_or('\0'));
if first_char.is_ascii_lowercase() {
return None;
}
let mut i = 0;
if StringIsArrayIndex(s, &mut i) {
Some(i)
} else {
None
}
/*let s = jsstr_to_string(cx, RUST_JSID_TO_STRING(raw_id));
if s.len() == 0 {
return None;
}
let first = s.chars().next().unwrap();
if first.is_ascii_lowercase() {
return None;
}
let mut i: u32 = 0;
let is_array = if s.is_ascii() {
let chars = s.as_bytes();
StringIsArrayIndex1(chars.as_ptr() as *const _, chars.len() as u32, &mut i)
} else {
let chars = s.encode_utf16().collect::<Vec<u16>>();
let slice = chars.as_slice();
StringIsArrayIndex2(slice.as_ptr(), chars.len() as u32, &mut i)
};
if is_array {
Some(i)
} else {
None
}*/
}
/// Find the enum equivelent of a string given by `v` in `pairs`.
/// Returns `Err(())` on JSAPI failure (there is a pending exception), and
/// `Ok((None, value))` if there was no matching string.
pub(crate) unsafe fn find_enum_value<'a, T>(
cx: *mut JSContext,
v: HandleValue,
pairs: &'a [(&'static str, T)],
) -> Result<(Option<&'a T>, DOMString), ()> {
match ptr::NonNull::new(ToString(cx, v)) {
Some(jsstr) => {
let search = jsstring_to_str(cx, jsstr);
Ok((
pairs
.iter()
.find(|&&(key, _)| search == *key)
.map(|(_, ev)| ev),
search,
))
},
None => Err(()),
}
}
/// Returns wether `obj` is a platform object using dynamic unwrap
/// <https://heycam.github.io/webidl/#dfn-platform-object>
#[allow(dead_code)]
@ -295,308 +103,6 @@ fn is_platform_object(
}
}
/// Get the property with name `property` from `object`.
/// Returns `Err(())` on JSAPI failure (there is a pending exception), and
/// `Ok(false)` if there was no property with the given name.
pub(crate) fn get_dictionary_property(
cx: *mut JSContext,
object: HandleObject,
property: &str,
rval: MutableHandleValue,
_can_gc: CanGc,
) -> Result<bool, ()> {
fn has_property(
cx: *mut JSContext,
object: HandleObject,
property: &CString,
found: &mut bool,
) -> bool {
unsafe { JS_HasProperty(cx, object, property.as_ptr(), found) }
}
fn get_property(
cx: *mut JSContext,
object: HandleObject,
property: &CString,
value: MutableHandleValue,
) -> bool {
unsafe { JS_GetProperty(cx, object, property.as_ptr(), value) }
}
let property = CString::new(property).unwrap();
if object.get().is_null() {
return Ok(false);
}
let mut found = false;
if !has_property(cx, object, &property, &mut found) {
return Err(());
}
if !found {
return Ok(false);
}
if !get_property(cx, object, &property, rval) {
return Err(());
}
Ok(true)
}
/// Set the property with name `property` from `object`.
/// Returns `Err(())` on JSAPI failure, or null object,
/// and Ok(()) otherwise
pub(crate) fn set_dictionary_property(
cx: *mut JSContext,
object: HandleObject,
property: &str,
value: HandleValue,
) -> Result<(), ()> {
if object.get().is_null() {
return Err(());
}
let property = CString::new(property).unwrap();
unsafe {
if !JS_SetProperty(cx, object, property.as_ptr(), value) {
return Err(());
}
}
Ok(())
}
/// Returns whether `proxy` has a property `id` on its prototype.
pub(crate) unsafe fn has_property_on_prototype(
cx: *mut JSContext,
proxy: HandleObject,
id: HandleId,
found: &mut bool,
) -> bool {
rooted!(in(cx) let mut proto = ptr::null_mut::<JSObject>());
if !JS_GetPrototype(cx, proxy, proto.handle_mut()) {
return false;
}
assert!(!proto.is_null());
JS_HasPropertyById(cx, proto.handle(), id, found)
}
/// Drop the resources held by reserved slots of a global object
pub(crate) unsafe fn finalize_global(obj: *mut JSObject) {
let protolist = get_proto_or_iface_array(obj);
let list = (*protolist).as_mut_ptr();
for idx in 0..PROTO_OR_IFACE_LENGTH as isize {
let entry = list.offset(idx);
let value = *entry;
<*mut JSObject>::post_barrier(entry, value, ptr::null_mut());
}
let _: Box<ProtoOrIfaceArray> = Box::from_raw(protolist);
}
/// Trace the resources held by reserved slots of a global object
pub(crate) unsafe fn trace_global(tracer: *mut JSTracer, obj: *mut JSObject) {
let array = get_proto_or_iface_array(obj);
for proto in (*array).iter() {
if !proto.is_null() {
trace_object(
tracer,
"prototype",
&*(proto as *const *mut JSObject as *const Heap<*mut JSObject>),
);
}
}
}
/// Enumerate lazy properties of a global object.
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_EnumerateStandardClasses(cx, obj) {
return false;
}
for init_fun in InterfaceObjectMap::MAP.values() {
init_fun(SafeJSContext::from_ptr(cx), Handle::from_raw(obj));
}
true
}
/// Resolve a lazy global property, for interface objects and named constructors.
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) {
return false;
}
if *rval {
return true;
}
if !id.is_string() {
*rval = false;
return true;
}
let string = id.to_string();
if !JS_DeprecatedStringHasLatin1Chars(string) {
*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(init_fun) = InterfaceObjectMap::MAP.get(bytes) {
init_fun(SafeJSContext::from_ptr(cx), Handle::from_raw(obj));
*rval = true;
} else {
*rval = false;
}
true
}
/// Deletes the property `id` from `object`.
pub(crate) unsafe fn delete_property_by_id(
cx: *mut JSContext,
object: HandleObject,
id: HandleId,
bp: *mut ObjectOpResult,
) -> bool {
JS_DeletePropertyById(cx, object, id, bp)
}
unsafe fn generic_call<const EXCEPTION_TO_REJECTION: bool>(
cx: *mut JSContext,
argc: libc::c_uint,
vp: *mut JSVal,
is_lenient: bool,
call: unsafe extern "C" fn(
*const JSJitInfo,
*mut JSContext,
RawHandleObject,
*mut libc::c_void,
u32,
*mut JSVal,
) -> bool,
can_gc: CanGc,
) -> bool {
let args = CallArgs::from_vp(vp, argc);
let info = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));
let proto_id = (*info).__bindgen_anon_2.protoID;
let cx = SafeJSContext::from_ptr(cx);
let thisobj = args.thisv();
if !thisobj.get().is_null_or_undefined() && !thisobj.get().is_object() {
throw_invalid_this(cx, proto_id);
return if EXCEPTION_TO_REJECTION {
exception_to_promise(*cx, args.rval(), can_gc)
} else {
false
};
}
rooted!(in(*cx) let obj = if thisobj.get().is_object() {
thisobj.get().to_object()
} else {
GetNonCCWObjectGlobal(JS_CALLEE(*cx, vp).to_object_or_null())
});
let depth = (*info).__bindgen_anon_3.depth as usize;
let proto_check = PrototypeCheck::Depth { depth, proto_id };
let this = match private_from_proto_check(obj.get(), *cx, proto_check) {
Ok(val) => val,
Err(()) => {
if is_lenient {
debug_assert!(!JS_IsExceptionPending(*cx));
*vp = UndefinedValue();
return true;
} else {
throw_invalid_this(cx, proto_id);
return if EXCEPTION_TO_REJECTION {
exception_to_promise(*cx, args.rval(), can_gc)
} else {
false
};
}
},
};
call(
info,
*cx,
obj.handle().into(),
this as *mut libc::c_void,
argc,
vp,
)
}
/// Generic method of IDL interface.
pub(crate) unsafe extern "C" fn generic_method<const EXCEPTION_TO_REJECTION: bool>(
cx: *mut JSContext,
argc: libc::c_uint,
vp: *mut JSVal,
) -> bool {
generic_call::<EXCEPTION_TO_REJECTION>(cx, argc, vp, false, CallJitMethodOp, CanGc::note())
}
/// Generic getter of IDL interface.
pub(crate) unsafe extern "C" fn generic_getter<const EXCEPTION_TO_REJECTION: bool>(
cx: *mut JSContext,
argc: libc::c_uint,
vp: *mut JSVal,
) -> bool {
generic_call::<EXCEPTION_TO_REJECTION>(cx, argc, vp, false, CallJitGetterOp, CanGc::note())
}
/// Generic lenient getter of IDL interface.
pub(crate) unsafe extern "C" fn generic_lenient_getter<const EXCEPTION_TO_REJECTION: bool>(
cx: *mut JSContext,
argc: libc::c_uint,
vp: *mut JSVal,
) -> bool {
generic_call::<EXCEPTION_TO_REJECTION>(cx, argc, vp, true, CallJitGetterOp, CanGc::note())
}
unsafe extern "C" fn call_setter(
info: *const JSJitInfo,
cx: *mut JSContext,
handle: RawHandleObject,
this: *mut libc::c_void,
argc: u32,
vp: *mut JSVal,
) -> bool {
if !CallJitSetterOp(info, cx, handle, this, argc, vp) {
return false;
}
*vp = UndefinedValue();
true
}
/// Generic setter of IDL interface.
pub(crate) unsafe extern "C" fn generic_setter(
cx: *mut JSContext,
argc: libc::c_uint,
vp: *mut JSVal,
) -> bool {
generic_call::<false>(cx, argc, vp, false, call_setter, CanGc::note())
}
/// Generic lenient setter of IDL interface.
pub(crate) unsafe extern "C" fn generic_lenient_setter(
cx: *mut JSContext,
argc: libc::c_uint,
vp: *mut JSVal,
) -> bool {
generic_call::<false>(cx, argc, vp, true, call_setter, CanGc::note())
}
unsafe extern "C" fn instance_class_has_proto_at_depth(
clasp: *const js::jsapi::JSClass,
proto_id: u32,
@ -612,83 +118,17 @@ pub(crate) const DOM_CALLBACKS: DOMCallbacks = DOMCallbacks {
instanceClassMatchesProto: Some(instance_class_has_proto_at_depth),
};
// Generic method for returning libc::c_void from caller
pub(crate) trait AsVoidPtr {
fn as_void_ptr(&self) -> *const libc::c_void;
}
impl<T> AsVoidPtr for T {
fn as_void_ptr(&self) -> *const libc::c_void {
self as *const T as *const libc::c_void
}
}
// Generic method for returning c_char from caller
pub(crate) trait AsCCharPtrPtr {
fn as_c_char_ptr(&self) -> *const c_char;
}
impl AsCCharPtrPtr for [u8] {
fn as_c_char_ptr(&self) -> *const c_char {
self as *const [u8] as *const c_char
}
}
/// <https://searchfox.org/mozilla-central/rev/7279a1df13a819be254fd4649e07c4ff93e4bd45/dom/bindings/BindingUtils.cpp#3300>
pub(crate) unsafe extern "C" fn generic_static_promise_method(
cx: *mut JSContext,
argc: libc::c_uint,
vp: *mut JSVal,
) -> bool {
let args = CallArgs::from_vp(vp, argc);
let info = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));
assert!(!info.is_null());
// TODO: we need safe wrappers for this in mozjs!
//assert_eq!((*info)._bitfield_1, JSJitInfo_OpType::StaticMethod as u8)
let static_fn = (*info).__bindgen_anon_1.staticMethod.unwrap();
if static_fn(cx, argc, vp) {
return true;
}
exception_to_promise(cx, args.rval(), CanGc::note())
}
/// Coverts exception to promise rejection
///
/// <https://searchfox.org/mozilla-central/rev/b220e40ff2ee3d10ce68e07d8a8a577d5558e2a2/dom/bindings/BindingUtils.cpp#3315>
pub(crate) unsafe fn exception_to_promise(
cx: *mut JSContext,
rval: RawMutableHandleValue,
/// Eagerly define all relevant WebIDL interface constructors on the
/// provided global object.
pub(crate) fn define_all_exposed_interfaces(
global: &GlobalScope,
_in_realm: InRealm,
_can_gc: CanGc,
) -> bool {
rooted!(in(cx) let mut exception = UndefinedValue());
if !JS_GetPendingException(cx, exception.handle_mut()) {
return false;
) {
let cx = GlobalScope::get_cx();
for (_, interface) in &InterfaceObjectMap::MAP {
(interface.define)(cx, global.reflector().get_jsobject());
}
JS_ClearPendingException(cx);
if let Some(promise) = NonNull::new(CallOriginalPromiseReject(cx, exception.handle())) {
promise.to_jsval(cx, MutableHandleValue::from_raw(rval));
true
} else {
// We just give up. Put the exception back.
JS_SetPendingException(cx, exception.handle(), ExceptionStackBehavior::Capture);
false
}
}
/// Operations that must be invoked from the generated bindings.
pub(crate) trait DomHelpers<D: DomTypes> {
fn throw_dom_exception(cx: SafeJSContext, global: &D::GlobalScope, result: Error);
unsafe fn call_html_constructor<T: DerivedFrom<D::Element> + DomObject>(
cx: SafeJSContext,
args: &CallArgs,
global: &D::GlobalScope,
proto_id: crate::dom::bindings::codegen::PrototypeList::ID,
creator: unsafe fn(SafeJSContext, HandleObject, *mut ProtoOrIfaceArray),
can_gc: CanGc,
) -> bool;
fn settings_stack() -> &'static LocalKey<RefCell<Vec<StackEntry<D>>>>;
}
impl DomHelpers<crate::DomTypeHolder> for crate::DomTypeHolder {
@ -696,11 +136,12 @@ impl DomHelpers<crate::DomTypeHolder> for crate::DomTypeHolder {
cx: SafeJSContext,
global: &<crate::DomTypeHolder as DomTypes>::GlobalScope,
result: Error,
can_gc: CanGc,
) {
throw_dom_exception(cx, global, result, CanGc::note())
throw_dom_exception(cx, global, result, can_gc)
}
unsafe fn call_html_constructor<
fn call_html_constructor<
T: DerivedFrom<<crate::DomTypeHolder as DomTypes>::Element> + DomObject,
>(
cx: SafeJSContext,
@ -716,4 +157,40 @@ impl DomHelpers<crate::DomTypeHolder> for crate::DomTypeHolder {
fn settings_stack() -> &'static LocalKey<RefCell<Vec<StackEntry<crate::DomTypeHolder>>>> {
&settings_stack::STACK
}
fn principals_callbacks() -> &'static JSPrincipalsCallbacks {
&PRINCIPALS_CALLBACKS
}
fn is_platform_object_same_origin(cx: SafeJSContext, obj: RawHandleObject) -> bool {
unsafe { is_platform_object_same_origin(cx, obj) }
}
fn interface_map() -> &'static phf::Map<&'static [u8], Interface> {
&InterfaceObjectMap::MAP
}
fn push_new_element_queue() {
push_new_element_queue()
}
fn pop_current_element_queue(can_gc: CanGc) {
pop_current_element_queue(can_gc)
}
fn reflect_dom_object<T, U>(obj: Box<T>, global: &U, can_gc: CanGc) -> DomRoot<T>
where
T: DomObject + DomObjectWrap<crate::DomTypeHolder>,
U: DerivedFrom<GlobalScope>,
{
reflect_dom_object(obj, global, can_gc)
}
fn report_pending_exception(
cx: SafeJSContext,
dispatch_event: bool,
realm: InRealm,
can_gc: CanGc,
) {
report_pending_exception(cx, dispatch_event, realm, can_gc)
}
}

View file

@ -8,7 +8,7 @@ use std::ops::{Deref, DerefMut, Drop};
use js::jsapi::JSTracer;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
pub use script_bindings::weakref::*;
pub(crate) use script_bindings::weakref::*;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::root::DomRoot;

View file

@ -4,7 +4,7 @@
//! Functions for validating and extracting qualified XML names.
use html5ever::{LocalName, Namespace, Prefix, namespace_url, ns};
use html5ever::{LocalName, Namespace, Prefix, ns};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::str::DOMString;

View file

@ -3,18 +3,17 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use std::num::NonZeroU32;
use std::ptr;
use std::rc::Rc;
use base::id::{BlobId, BlobIndex, PipelineNamespaceId};
use base::id::{BlobId, BlobIndex};
use constellation_traits::{BlobData, BlobImpl};
use dom_struct::dom_struct;
use encoding_rs::UTF_8;
use js::jsapi::JSObject;
use js::rust::HandleObject;
use js::typedarray::Uint8;
use net_traits::filemanager_thread::RelativePos;
use script_traits::serializable::BlobImpl;
use uuid::Uuid;
use crate::body::{FetchedData, run_array_buffer_data_algorithm};
@ -25,16 +24,16 @@ use crate::dom::bindings::codegen::UnionTypes::ArrayBufferOrArrayBufferViewOrBlo
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::serializable::{IntoStorageKey, Serializable, StorageKey};
use crate::dom::bindings::serializable::Serializable;
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone::{StructuredData, StructuredDataReader};
use crate::dom::bindings::structuredclone::StructuredData;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::readablestream::ReadableStream;
use crate::realms::{AlreadyInRealm, InRealm};
use crate::script_runtime::CanGc;
// https://w3c.github.io/FileAPI/#blob
/// <https://w3c.github.io/FileAPI/#dfn-Blob>
#[dom_struct]
pub(crate) struct Blob {
reflector_: Reflector,
@ -94,7 +93,7 @@ impl Blob {
}
impl Serializable for Blob {
type Id = BlobId;
type Index = BlobIndex;
type Data = BlobImpl;
/// <https://w3c.github.io/FileAPI/#ref-for-serialization-steps>
@ -120,40 +119,14 @@ impl Serializable for Blob {
Ok(deserialized_blob)
}
fn serialized_storage(
reader: StructuredData<'_>,
) -> &mut Option<HashMap<Self::Id, Self::Data>> {
fn serialized_storage<'a>(
reader: StructuredData<'a, '_>,
) -> &'a mut Option<HashMap<BlobId, Self::Data>> {
match reader {
StructuredData::Reader(r) => &mut r.blob_impls,
StructuredData::Writer(w) => &mut w.blobs,
}
}
fn deserialized_storage(
data: &mut StructuredDataReader,
) -> &mut Option<HashMap<StorageKey, DomRoot<Self>>> {
&mut data.blobs
}
}
impl From<StorageKey> for BlobId {
fn from(storage_key: StorageKey) -> BlobId {
let namespace_id = PipelineNamespaceId(storage_key.name_space);
let index =
BlobIndex(NonZeroU32::new(storage_key.index).expect("Deserialized blob index is zero"));
BlobId {
namespace_id,
index,
}
}
}
impl IntoStorageKey for BlobId {
fn into_storage_key(self) -> StorageKey {
let BlobIndex(index) = self.index;
StorageKey::new(self.namespace_id, index)
}
}
/// Extract bytes from BlobParts, used by Blob and File constructor
@ -225,7 +198,7 @@ impl BlobMethods<crate::DomTypeHolder> for Blob {
self.get_stream(can_gc)
}
// https://w3c.github.io/FileAPI/#slice-method-algo
/// <https://w3c.github.io/FileAPI/#slice-method-algo>
fn Slice(
&self,
start: Option<i64>,
@ -233,17 +206,30 @@ impl BlobMethods<crate::DomTypeHolder> for Blob {
content_type: Option<DOMString>,
can_gc: CanGc,
) -> DomRoot<Blob> {
let type_string =
normalize_type_string(content_type.unwrap_or(DOMString::from("")).as_ref());
let rel_pos = RelativePos::from_opts(start, end);
let blob_impl = BlobImpl::new_sliced(rel_pos, self.blob_id, type_string);
Blob::new(&self.global(), blob_impl, can_gc)
let global = self.global();
let type_string = normalize_type_string(&content_type.unwrap_or_default());
// If our parent is already a sliced blob then we reference the data from the grandparent instead,
// to keep the blob ancestry chain short.
let (parent, range) = match *global.get_blob_data(&self.blob_id) {
BlobData::Sliced(grandparent, parent_range) => {
let range = RelativePos {
start: parent_range.start + start.unwrap_or_default(),
end: end.map(|end| end + parent_range.start).or(parent_range.end),
};
(grandparent, range)
},
_ => (self.blob_id, RelativePos::from_opts(start, end)),
};
let blob_impl = BlobImpl::new_sliced(range, parent, type_string);
Blob::new(&global, blob_impl, can_gc)
}
// https://w3c.github.io/FileAPI/#text-method-algo
fn Text(&self, can_gc: CanGc) -> Rc<Promise> {
let global = self.global();
let in_realm_proof = AlreadyInRealm::assert();
let in_realm_proof = AlreadyInRealm::assert::<crate::DomTypeHolder>();
let p = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc);
let id = self.get_blob_url_id();
global.read_file_async(
@ -266,7 +252,7 @@ impl BlobMethods<crate::DomTypeHolder> for Blob {
// https://w3c.github.io/FileAPI/#arraybuffer-method-algo
fn ArrayBuffer(&self, can_gc: CanGc) -> Rc<Promise> {
let global = self.global();
let in_realm_proof = AlreadyInRealm::assert();
let in_realm_proof = AlreadyInRealm::assert::<crate::DomTypeHolder>();
let p = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc);
let id = self.get_blob_url_id();

View file

@ -300,7 +300,7 @@ where
T: AsyncBluetoothListener + DomObject + 'static,
F: FnOnce(StringOrUnsignedLong) -> Fallible<UUID>,
{
let in_realm_proof = AlreadyInRealm::assert();
let in_realm_proof = AlreadyInRealm::assert::<crate::DomTypeHolder>();
let p = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc);
let result_uuid = if let Some(u) = uuid {

View file

@ -4,9 +4,9 @@
use std::cell::Cell;
use constellation_traits::BroadcastMsg;
use dom_struct::dom_struct;
use js::rust::{HandleObject, HandleValue};
use script_traits::BroadcastMsg;
use uuid::Uuid;
use crate::dom::bindings::codegen::Bindings::BroadcastChannelBinding::BroadcastChannelMethods;

View file

@ -4,12 +4,12 @@
use canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg, FromScriptMsg};
use dom_struct::dom_struct;
use euclid::default::{Point2D, Rect, Size2D};
use ipc_channel::ipc::IpcSharedMemory;
use euclid::default::Size2D;
use profile_traits::ipc;
use script_bindings::inheritance::Castable;
use script_layout_interface::HTMLCanvasDataSource;
use servo_url::ServoUrl;
use snapshot::Snapshot;
use webrender_api::ImageKey;
use crate::canvas_context::{CanvasContext, CanvasHelpers, LayoutCanvasRenderingContextHelpers};
use crate::canvas_state::CanvasState;
@ -74,23 +74,12 @@ impl CanvasRenderingContext2D {
reflect_dom_object(boxed, global, can_gc)
}
// https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions
pub(crate) fn set_bitmap_dimensions(&self, size: Size2D<u32>) {
self.reset_to_initial_state();
self.canvas_state
.get_ipc_renderer()
.send(CanvasMsg::Recreate(
Some(size.to_u64()),
self.canvas_state.get_canvas_id(),
))
.unwrap();
}
// https://html.spec.whatwg.org/multipage/#reset-the-rendering-context-to-its-default-state
fn reset_to_initial_state(&self) {
self.canvas_state.reset_to_initial_state();
}
/// <https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions>
pub(crate) fn set_canvas_bitmap_dimensions(&self, size: Size2D<u64>) {
self.canvas_state.set_bitmap_dimensions(size);
}
@ -106,20 +95,17 @@ impl CanvasRenderingContext2D {
pub(crate) fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) {
self.canvas_state.send_canvas_2d_msg(msg)
}
pub(crate) fn get_rect(&self, rect: Rect<u32>) -> Vec<u8> {
let rect = Rect::new(
Point2D::new(rect.origin.x as u64, rect.origin.y as u64),
Size2D::new(rect.size.width as u64, rect.size.height as u64),
);
self.canvas_state.get_rect(self.canvas.size(), rect)
}
}
impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, CanvasRenderingContext2D> {
fn canvas_data_source(self) -> HTMLCanvasDataSource {
fn canvas_data_source(self) -> Option<ImageKey> {
let canvas_state = &self.unsafe_get().canvas_state;
HTMLCanvasDataSource::Image(canvas_state.image_key())
if canvas_state.is_paintable() {
Some(canvas_state.image_key())
} else {
None
}
}
}
@ -139,19 +125,19 @@ impl CanvasContext for CanvasRenderingContext2D {
}
fn resize(&self) {
self.set_bitmap_dimensions(self.size().cast())
self.set_canvas_bitmap_dimensions(self.size().cast())
}
fn get_image_data_as_shared_memory(&self) -> Option<IpcSharedMemory> {
fn get_image_data(&self) -> Option<Snapshot> {
if !self.canvas_state.is_paintable() {
return None;
}
let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
let msg = CanvasMsg::FromScript(FromScriptMsg::SendPixels(sender), self.get_canvas_id());
self.canvas_state.get_ipc_renderer().send(msg).unwrap();
Some(receiver.recv().unwrap())
}
fn get_image_data(&self) -> Option<Vec<u8>> {
Some(self.get_rect(Rect::from_size(self.size().cast())))
Some(receiver.recv().unwrap().to_owned())
}
fn origin_is_clean(&self) -> bool {
@ -642,6 +628,26 @@ impl CanvasRenderingContext2DMethods<crate::DomTypeHolder> for CanvasRenderingCo
self.canvas_state.set_miter_limit(limit)
}
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-setlinedash>
fn SetLineDash(&self, segments: Vec<f64>) {
self.canvas_state.set_line_dash(segments);
}
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-getlinedash>
fn GetLineDash(&self) -> Vec<f64> {
self.canvas_state.line_dash()
}
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-linedashoffset>
fn LineDashOffset(&self) -> f64 {
self.canvas_state.line_dash_offset()
}
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-linedashoffset>
fn SetLineDashOffset(&self, offset: f64) {
self.canvas_state.set_line_dash_offset(offset);
}
// https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx
fn ShadowOffsetX(&self) -> f64 {
self.canvas_state.shadow_offset_x()

View file

@ -6,12 +6,12 @@
use std::cell::LazyCell;
use dom_struct::dom_struct;
use script_bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId, TextTypeId};
use crate::dom::bindings::cell::{DomRefCell, Ref};
use crate::dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
use crate::dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
use crate::dom::bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId, TextTypeId};
use crate::dom::bindings::codegen::UnionTypes::NodeOrString;
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
use crate::dom::bindings::inheritance::Castable;
@ -260,9 +260,9 @@ impl CharacterDataMethods<crate::DomTypeHolder> for CharacterData {
}
// https://dom.spec.whatwg.org/#dom-childnode-remove
fn Remove(&self) {
fn Remove(&self, can_gc: CanGc) {
let node = self.upcast::<Node>();
node.remove_self();
node.remove_self(can_gc);
}
// https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-previouselementsibling

View file

@ -0,0 +1,282 @@
/* 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 std::rc::Rc;
use std::str::FromStr;
use constellation_traits::BlobImpl;
use data_url::mime::Mime;
use dom_struct::dom_struct;
use embedder_traits::EmbedderMsg;
use js::rust::HandleValue as SafeHandleValue;
use crate::dom::bindings::codegen::Bindings::ClipboardBinding::{
ClipboardMethods, PresentationStyle,
};
use crate::dom::bindings::error::Error;
use crate::dom::bindings::refcounted::TrustedPromise;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::blob::Blob;
use crate::dom::clipboarditem::Representation;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
use crate::dom::window::Window;
use crate::realms::{InRealm, enter_realm};
use crate::routed_promise::{RoutedPromiseListener, route_promise};
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
/// The fulfillment handler for the reacting to representationDataPromise part of
/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext>.
#[derive(Clone, JSTraceable, MallocSizeOf)]
struct RepresentationDataPromiseFulfillmentHandler {
#[ignore_malloc_size_of = "Rc are hard"]
promise: Rc<Promise>,
}
impl Callback for RepresentationDataPromiseFulfillmentHandler {
/// The fulfillment case of Step 3.4.1.1.4.3 of
/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext>.
fn callback(&self, cx: SafeJSContext, v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
// If v is a DOMString, then follow the below steps:
// Resolve p with v.
// Return p.
self.promise.resolve(cx, v, can_gc);
// NOTE: Since we ask text from arboard, v can't be a Blob
// If v is a Blob, then follow the below steps:
// Let string be the result of UTF-8 decoding vs underlying byte sequence.
// Resolve p with string.
// Return p.
}
}
/// The rejection handler for the reacting to representationDataPromise part of
/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext>.
#[derive(Clone, JSTraceable, MallocSizeOf)]
struct RepresentationDataPromiseRejectionHandler {
#[ignore_malloc_size_of = "Rc are hard"]
promise: Rc<Promise>,
}
impl Callback for RepresentationDataPromiseRejectionHandler {
/// The rejection case of Step 3.4.1.1.4.3 of
/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext>.
fn callback(&self, _cx: SafeJSContext, _v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
// Reject p with "NotFoundError" DOMException in realm.
// Return p.
self.promise.reject_error(Error::NotFound, can_gc);
}
}
#[dom_struct]
pub(crate) struct Clipboard {
event_target: EventTarget,
}
impl Clipboard {
fn new_inherited() -> Clipboard {
Clipboard {
event_target: EventTarget::new_inherited(),
}
}
pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<Clipboard> {
reflect_dom_object(Box::new(Clipboard::new_inherited()), global, can_gc)
}
}
impl ClipboardMethods<crate::DomTypeHolder> for Clipboard {
/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext>
fn ReadText(&self, can_gc: CanGc) -> Rc<Promise> {
// Step 1 Let realm be this's relevant realm.
let global = self.global();
// Step 2 Let p be a new promise in realm.
let p = Promise::new(&global, can_gc);
// Step 3 Run the following steps in parallel:
// TODO Step 3.1 Let r be the result of running check clipboard read permission.
// Step 3.2 If r is false, then:
// Step 3.2.1 Queue a global task on the permission task source, given realms global object,
// to reject p with "NotAllowedError" DOMException in realm.
// Step 3.2.2 Abort these steps.
// Step 3.3 Let data be a copy of the system clipboard data.
let window = global.as_window();
let sender = route_promise(&p, self, global.task_manager().clipboard_task_source());
window.send_to_embedder(EmbedderMsg::GetClipboardText(window.webview_id(), sender));
// Step 3.4 Queue a global task on the clipboard task source,
// given realms global object, to perform the below steps:
// NOTE: We queue the task inside route_promise and perform the steps inside handle_response
p
}
/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-writetext>
fn WriteText(&self, data: DOMString, can_gc: CanGc) -> Rc<Promise> {
// Step 1 Let realm be this's relevant realm.
// Step 2 Let p be a new promise in realm.
let p = Promise::new(&self.global(), can_gc);
// Step 3 Run the following steps in parallel:
// TODO write permission could be removed from spec
// Step 3.1 Let r be the result of running check clipboard write permission.
// Step 3.2 If r is false, then:
// Step 3.2.1 Queue a global task on the permission task source, given realms global object,
// to reject p with "NotAllowedError" DOMException in realm.
// Step 3.2.2 Abort these steps.
let trusted_promise = TrustedPromise::new(p.clone());
let bytes = Vec::from(data);
// Step 3.3 Queue a global task on the clipboard task source,
// given realms global object, to perform the below steps:
self.global().task_manager().clipboard_task_source().queue(
task!(write_to_system_clipboard: move || {
let promise = trusted_promise.root();
let global = promise.global();
// Step 3.3.1 Let itemList be an empty sequence<Blob>.
let mut item_list = Vec::new();
// Step 3.3.2 Let textBlob be a new Blob created with: type attribute set to "text/plain;charset=utf-8",
// and its underlying byte sequence set to the UTF-8 encoding of data.
let text_blob = Blob::new(
&global,
BlobImpl::new_from_bytes(bytes, "text/plain;charset=utf-8".into()),
CanGc::note(),
);
// Step 3.3.3 Add textBlob to itemList.
item_list.push(text_blob);
// Step 3.3.4 Let option be set to "unspecified".
let option = PresentationStyle::Unspecified;
// Step 3.3.5 Write blobs and option to the clipboard with itemList and option.
write_blobs_and_option_to_the_clipboard(global.as_window(), item_list, option);
// Step 3.3.6 Resolve p.
promise.resolve_native(&(), CanGc::note());
}),
);
// Step 3.4 Return p.
p
}
}
impl RoutedPromiseListener<Result<String, String>> for Clipboard {
fn handle_response(
&self,
response: Result<String, String>,
promise: &Rc<Promise>,
can_gc: CanGc,
) {
let global = self.global();
let text = response.unwrap_or_default();
// Step 3.4.1 For each systemClipboardItem in data:
// Step 3.4.1.1 For each systemClipboardRepresentation in systemClipboardItem:
// TODO: Arboard provide the first item that has a String representation
// Step 3.4.1.1.1 Let mimeType be the result of running the
// well-known mime type from os specific format algorithm given systemClipboardRepresentations name.
// Note: This is done by arboard, so we just convert the format to a MIME
let mime_type = Mime::from_str("text/plain").unwrap();
// Step 3.4.1.1.2 If mimeType is null, continue this loop.
// Note: Since the previous step is infallible, we don't need to handle this case
// Step 3.4.1.1.3 Let representation be a new representation.
let representation = Representation {
mime_type,
is_custom: false,
data: Promise::new_resolved(
&global,
GlobalScope::get_cx(),
DOMString::from(text),
can_gc,
),
};
// Step 3.4.1.1.4 If representations MIME type essence is "text/plain", then:
// Step 3.4.1.1.4.1 Set representations MIME type to mimeType.
// Note: Done when creating a new representation
// Step 3.4.1.1.4.2 Let representationDataPromise be the representations data.
// Step 3.4.1.1.4.3 React to representationDataPromise:
let fulfillment_handler = Box::new(RepresentationDataPromiseFulfillmentHandler {
promise: promise.clone(),
});
let rejection_handler = Box::new(RepresentationDataPromiseRejectionHandler {
promise: promise.clone(),
});
let handler = PromiseNativeHandler::new(
&global,
Some(fulfillment_handler),
Some(rejection_handler),
can_gc,
);
let realm = enter_realm(&*global);
let comp = InRealm::Entered(&realm);
representation
.data
.append_native_handler(&handler, comp, can_gc);
// Step 3.4.2 Reject p with "NotFoundError" DOMException in realm.
// Step 3.4.3 Return p.
// NOTE: We follow the same behaviour of Gecko by doing nothing if no text is available instead of rejecting p
}
}
/// <https://w3c.github.io/clipboard-apis/#write-blobs-and-option-to-the-clipboard>
fn write_blobs_and_option_to_the_clipboard(
window: &Window,
items: Vec<DomRoot<Blob>>,
_presentation_style: PresentationStyle,
) {
// TODO Step 1 Let webCustomFormats be a sequence<Blob>.
// Step 2 For each item in items:
for item in items {
// TODO support more formats than just text/plain
// Step 2.1 Let formatString be the result of running os specific well-known format given items type.
// Step 2.2 If formatString is empty then follow the below steps:
// Step 2.2.1 Let webCustomFormatString be the items type.
// Step 2.2.2 Let webCustomFormat be an empty type.
// Step 2.2.3 If webCustomFormatString starts with "web " prefix,
// then remove the "web " prefix and store the remaining string in webMimeTypeString.
// Step 2.2.4 Let webMimeType be the result of parsing a MIME type given webMimeTypeString.
// Step 2.2.5 If webMimeType is failure, then abort all steps.
// Step 2.2.6 Let webCustomFormats type's essence equal to webMimeType.
// Step 2.2.7 Set items type to webCustomFormat.
// Step 2.2.8 Append webCustomFormat to webCustomFormats.
// Step 2.3 Let payload be the result of UTF-8 decoding items underlying byte sequence.
// Step 2.4 Insert payload and presentationStyle into the system clipboard
// using formatString as the native clipboard format.
window.send_to_embedder(EmbedderMsg::SetClipboardText(
window.webview_id(),
String::from_utf8(
item.get_bytes()
.expect("No bytes found for Blob created by caller"),
)
.expect("DOMString contained invalid bytes"),
));
}
// TODO Step 3 Write web custom formats given webCustomFormats.
// Needs support to arbitrary formats inside arboard
}

View file

@ -41,7 +41,7 @@ impl ClipboardEventType {
}
#[dom_struct]
pub struct ClipboardEvent {
pub(crate) struct ClipboardEvent {
event: Event,
clipboard_data: MutNullableDom<DataTransfer>,
}

View file

@ -0,0 +1,181 @@
/* 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 std::ops::Deref;
use std::rc::Rc;
use std::str::FromStr;
use data_url::mime::Mime;
use dom_struct::dom_struct;
use js::rust::{HandleObject, MutableHandleValue};
use script_bindings::record::Record;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::ClipboardBinding::{
ClipboardItemMethods, ClipboardItemOptions, PresentationStyle,
};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::frozenarray::CachedFrozenArray;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::promise::Promise;
use crate::dom::window::Window;
use crate::script_runtime::{CanGc, JSContext};
/// <https://w3c.github.io/clipboard-apis/#web-custom-format>
const CUSTOM_FORMAT_PREFIX: &str = "web ";
/// <https://w3c.github.io/clipboard-apis/#representation>
#[derive(JSTraceable, MallocSizeOf)]
pub(super) struct Representation {
#[no_trace]
#[ignore_malloc_size_of = "Extern type"]
pub mime_type: Mime,
pub is_custom: bool,
#[ignore_malloc_size_of = "Rc is hard"]
pub data: Rc<Promise>,
}
#[dom_struct]
pub(crate) struct ClipboardItem {
reflector_: Reflector,
representations: DomRefCell<Vec<Representation>>,
presentation_style: DomRefCell<PresentationStyle>,
#[ignore_malloc_size_of = "mozjs"]
frozen_types: CachedFrozenArray,
}
impl ClipboardItem {
fn new_inherited() -> ClipboardItem {
ClipboardItem {
reflector_: Reflector::new(),
representations: Default::default(),
presentation_style: Default::default(),
frozen_types: CachedFrozenArray::new(),
}
}
fn new(window: &Window, proto: Option<HandleObject>, can_gc: CanGc) -> DomRoot<ClipboardItem> {
reflect_dom_object_with_proto(
Box::new(ClipboardItem::new_inherited()),
window,
proto,
can_gc,
)
}
}
impl ClipboardItemMethods<crate::DomTypeHolder> for ClipboardItem {
/// <https://w3c.github.io/clipboard-apis/#dom-clipboarditem-clipboarditem>
fn Constructor(
global: &Window,
proto: Option<HandleObject>,
can_gc: CanGc,
items: Record<DOMString, Rc<Promise>>,
options: &ClipboardItemOptions,
) -> Fallible<DomRoot<ClipboardItem>> {
// Step 1 If items is empty, then throw a TypeError.
if items.is_empty() {
return Err(Error::Type(String::from("No item provided")));
}
// Step 2 If options is empty, then set options["presentationStyle"] = "unspecified".
// NOTE: This is done inside bindings
// Step 3 Set this's clipboard item to a new clipboard item.
let clipboard_item = ClipboardItem::new(global, proto, can_gc);
// Step 4 Set this's clipboard item's presentation style to options["presentationStyle"].
*clipboard_item.presentation_style.borrow_mut() = options.presentationStyle;
// Step 6 For each (key, value) in items:
for (key, value) in items.deref() {
// Step 6.2 Let isCustom be false.
// Step 6.3 If key starts with `"web "` prefix, then
// Step 6.3.1 Remove `"web "` prefix and assign the remaining string to key.
let (key, is_custom) = match key.strip_prefix(CUSTOM_FORMAT_PREFIX) {
None => (key.str(), false),
// Step 6.3.2 Set isCustom true
Some(stripped) => (stripped, true),
};
// Step 6.5 Let mimeType be the result of parsing a MIME type given key.
// Step 6.6 If mimeType is failure, then throw a TypeError.
let mime_type =
Mime::from_str(key).map_err(|_| Error::Type(String::from("Invalid mime type")))?;
// Step 6.7 If this's clipboard item's list of representations contains a representation
// whose MIME type is mimeType and whose [representation/isCustom] is isCustom, then throw a TypeError.
if clipboard_item
.representations
.borrow()
.iter()
.any(|representation| {
representation.mime_type == mime_type && representation.is_custom == is_custom
})
{
return Err(Error::Type(String::from("Tried to add a duplicate mime")));
}
// Step 6.1 Let representation be a new representation.
// Step 6.4 Set representations isCustom flag to isCustom.
// Step 6.8 Set representations MIME type to mimeType.
// Step 6.9 Set representations data to value.
let representation = Representation {
mime_type,
is_custom,
data: value.clone(),
};
// Step 6.10 Append representation to this's clipboard item's list of representations.
clipboard_item
.representations
.borrow_mut()
.push(representation);
}
// NOTE: The steps for creating a frozen array from the list of mimeType are done in the Types() method
Ok(clipboard_item)
}
/// <https://w3c.github.io/clipboard-apis/#dom-clipboarditem-presentationstyle>
fn PresentationStyle(&self) -> PresentationStyle {
*self.presentation_style.borrow()
}
/// <https://w3c.github.io/clipboard-apis/#dom-clipboarditem-types>
fn Types(&self, cx: JSContext, can_gc: CanGc, retval: MutableHandleValue) {
self.frozen_types.get_or_init(
|| {
// Step 5 Let types be a list of DOMString.
let mut types = Vec::new();
self.representations
.borrow()
.iter()
.for_each(|representation| {
// Step 6.11 Let mimeTypeString be the result of serializing a MIME type with mimeType.
let mime_type_string = representation.mime_type.to_string();
// Step 6.12 If isCustom is true, prefix mimeTypeString with `"web "`.
let mime_type_string = if representation.is_custom {
format!("{}{}", CUSTOM_FORMAT_PREFIX, mime_type_string)
} else {
mime_type_string
};
// Step 6.13 Add mimeTypeString to types.
types.push(DOMString::from(mime_type_string));
});
types
},
cx,
retval,
can_gc,
);
}
}

View file

@ -2,7 +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 html5ever::{LocalName, Prefix, QualName, local_name, namespace_url, ns};
use html5ever::{LocalName, Prefix, QualName, local_name, ns};
use js::rust::HandleObject;
use servo_config::pref;
@ -85,6 +85,7 @@ use crate::dom::htmlulistelement::HTMLUListElement;
use crate::dom::htmlunknownelement::HTMLUnknownElement;
use crate::dom::htmlvideoelement::HTMLVideoElement;
use crate::dom::svgelement::SVGElement;
use crate::dom::svgimageelement::SVGImageElement;
use crate::dom::svgsvgelement::SVGSVGElement;
use crate::realms::{InRealm, enter_realm};
use crate::script_runtime::CanGc;
@ -114,6 +115,7 @@ fn create_svg_element(
}
match name.local {
local_name!("image") => make!(SVGImageElement),
local_name!("svg") => make!(SVGSVGElement),
_ => make!(SVGElement),
}

View file

@ -18,6 +18,9 @@ use crate::dom::csssupportsrule::CSSSupportsRule;
#[dom_struct]
pub(crate) struct CSSConditionRule {
cssgroupingrule: CSSGroupingRule,
#[ignore_malloc_size_of = "Arc"]
#[no_trace]
rules: Arc<Locked<StyleCssRules>>,
}
impl CSSConditionRule {
@ -26,7 +29,8 @@ impl CSSConditionRule {
rules: Arc<Locked<StyleCssRules>>,
) -> CSSConditionRule {
CSSConditionRule {
cssgroupingrule: CSSGroupingRule::new_inherited(parent_stylesheet, rules),
cssgroupingrule: CSSGroupingRule::new_inherited(parent_stylesheet),
rules,
}
}
@ -37,6 +41,10 @@ impl CSSConditionRule {
pub(crate) fn shared_lock(&self) -> &SharedRwLock {
self.cssgroupingrule.shared_lock()
}
pub(crate) fn clone_rules(&self) -> Arc<Locked<StyleCssRules>> {
self.rules.clone()
}
}
impl CSSConditionRuleMethods<crate::DomTypeHolder> for CSSConditionRule {

View file

@ -3,9 +3,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use servo_arc::Arc;
use style::shared_lock::{Locked, SharedRwLock};
use style::stylesheets::{CssRuleType, CssRuleTypes, CssRules as StyleCssRules};
use style::shared_lock::SharedRwLock;
use style::stylesheets::{CssRuleType, CssRuleTypes};
use crate::dom::bindings::codegen::Bindings::CSSGroupingRuleBinding::CSSGroupingRuleMethods;
use crate::dom::bindings::error::{ErrorResult, Fallible};
@ -13,28 +12,24 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::DomGlobal;
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::cssconditionrule::CSSConditionRule;
use crate::dom::csslayerblockrule::CSSLayerBlockRule;
use crate::dom::cssrule::CSSRule;
use crate::dom::cssrulelist::{CSSRuleList, RulesSource};
use crate::dom::cssstylerule::CSSStyleRule;
use crate::dom::cssstylesheet::CSSStyleSheet;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct CSSGroupingRule {
cssrule: CSSRule,
#[ignore_malloc_size_of = "Arc"]
#[no_trace]
rules: Arc<Locked<StyleCssRules>>,
rulelist: MutNullableDom<CSSRuleList>,
}
impl CSSGroupingRule {
pub(crate) fn new_inherited(
parent_stylesheet: &CSSStyleSheet,
rules: Arc<Locked<StyleCssRules>>,
) -> CSSGroupingRule {
pub(crate) fn new_inherited(parent_stylesheet: &CSSStyleSheet) -> CSSGroupingRule {
CSSGroupingRule {
cssrule: CSSRule::new_inherited(parent_stylesheet),
rules,
rulelist: MutNullableDom::new(None),
}
}
@ -42,10 +37,19 @@ impl CSSGroupingRule {
fn rulelist(&self, can_gc: CanGc) -> DomRoot<CSSRuleList> {
let parent_stylesheet = self.upcast::<CSSRule>().parent_stylesheet();
self.rulelist.or_init(|| {
let rules = if let Some(rule) = self.downcast::<CSSConditionRule>() {
rule.clone_rules()
} else if let Some(rule) = self.downcast::<CSSLayerBlockRule>() {
rule.clone_rules()
} else if let Some(rule) = self.downcast::<CSSStyleRule>() {
rule.ensure_rules()
} else {
unreachable!()
};
CSSRuleList::new(
self.global().as_window(),
parent_stylesheet,
RulesSource::Rules(self.rules.clone()),
RulesSource::Rules(rules),
can_gc,
)
})

View file

@ -4,8 +4,8 @@
use dom_struct::dom_struct;
use servo_arc::Arc;
use style::shared_lock::ToCssWithGuard;
use style::stylesheets::{CssRuleType, LayerBlockRule};
use style::shared_lock::{Locked, ToCssWithGuard};
use style::stylesheets::{CssRuleType, CssRules, LayerBlockRule};
use style_traits::ToCss;
use crate::dom::bindings::codegen::Bindings::CSSLayerBlockRuleBinding::CSSLayerBlockRuleMethods;
@ -32,10 +32,7 @@ impl CSSLayerBlockRule {
layerblockrule: Arc<LayerBlockRule>,
) -> CSSLayerBlockRule {
CSSLayerBlockRule {
cssgroupingrule: CSSGroupingRule::new_inherited(
parent_stylesheet,
layerblockrule.rules.clone(),
),
cssgroupingrule: CSSGroupingRule::new_inherited(parent_stylesheet),
layerblockrule,
}
}
@ -56,6 +53,10 @@ impl CSSLayerBlockRule {
can_gc,
)
}
pub(crate) fn clone_rules(&self) -> Arc<Locked<CssRules>> {
self.layerblockrule.rules.clone()
}
}
impl SpecificCSSRule for CSSLayerBlockRule {

View file

@ -0,0 +1,91 @@
/* 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 dom_struct::dom_struct;
use servo_arc::Arc;
use style::shared_lock::{Locked, ToCssWithGuard};
use style::stylesheets::{CssRuleType, NestedDeclarationsRule};
use crate::dom::bindings::codegen::Bindings::CSSNestedDeclarationsBinding::CSSNestedDeclarationsMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::cssrule::{CSSRule, SpecificCSSRule};
use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
use crate::dom::cssstylesheet::CSSStyleSheet;
use crate::dom::window::Window;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct CSSNestedDeclarations {
cssrule: CSSRule,
#[ignore_malloc_size_of = "Arc"]
#[no_trace]
nesteddeclarationsrule: Arc<Locked<NestedDeclarationsRule>>,
style_decl: MutNullableDom<CSSStyleDeclaration>,
}
impl CSSNestedDeclarations {
pub(crate) fn new_inherited(
parent_stylesheet: &CSSStyleSheet,
nesteddeclarationsrule: Arc<Locked<NestedDeclarationsRule>>,
) -> Self {
Self {
cssrule: CSSRule::new_inherited(parent_stylesheet),
nesteddeclarationsrule,
style_decl: Default::default(),
}
}
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn new(
window: &Window,
parent_stylesheet: &CSSStyleSheet,
nesteddeclarationsrule: Arc<Locked<NestedDeclarationsRule>>,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object(
Box::new(Self::new_inherited(
parent_stylesheet,
nesteddeclarationsrule,
)),
window,
can_gc,
)
}
}
impl SpecificCSSRule for CSSNestedDeclarations {
fn ty(&self) -> CssRuleType {
CssRuleType::NestedDeclarations
}
fn get_css(&self) -> DOMString {
let guard = self.cssrule.shared_lock().read();
self.nesteddeclarationsrule
.read_with(&guard)
.to_css_string(&guard)
.into()
}
}
impl CSSNestedDeclarationsMethods<crate::DomTypeHolder> for CSSNestedDeclarations {
/// <https://drafts.csswg.org/css-nesting/#dom-cssnesteddeclarations-style>
fn Style(&self, can_gc: CanGc) -> DomRoot<CSSStyleDeclaration> {
self.style_decl.or_init(|| {
let guard = self.cssrule.shared_lock().read();
CSSStyleDeclaration::new(
self.global().as_window(),
CSSStyleOwner::CSSRule(
Dom::from_ref(self.upcast()),
self.nesteddeclarationsrule.read_with(&guard).block.clone(),
),
None,
CSSModificationAccess::ReadWrite,
can_gc,
)
})
}
}

View file

@ -21,6 +21,7 @@ use crate::dom::csslayerblockrule::CSSLayerBlockRule;
use crate::dom::csslayerstatementrule::CSSLayerStatementRule;
use crate::dom::cssmediarule::CSSMediaRule;
use crate::dom::cssnamespacerule::CSSNamespaceRule;
use crate::dom::cssnesteddeclarations::CSSNestedDeclarations;
use crate::dom::cssstylerule::CSSStyleRule;
use crate::dom::cssstylesheet::CSSStyleSheet;
use crate::dom::csssupportsrule::CSSSupportsRule;
@ -69,6 +70,8 @@ impl CSSRule {
rule as &dyn SpecificCSSRule
} else if let Some(rule) = self.downcast::<CSSLayerStatementRule>() {
rule as &dyn SpecificCSSRule
} else if let Some(rule) = self.downcast::<CSSNestedDeclarations>() {
rule as &dyn SpecificCSSRule
} else {
unreachable!()
}
@ -125,7 +128,12 @@ impl CSSRule {
StyleCssRule::Scope(_) => unimplemented!(), // TODO
StyleCssRule::StartingStyle(_) => unimplemented!(), // TODO
StyleCssRule::PositionTry(_) => unimplemented!(), // TODO
StyleCssRule::NestedDeclarations(_) => unimplemented!(), // TODO
StyleCssRule::NestedDeclarations(s) => DomRoot::upcast(CSSNestedDeclarations::new(
window,
parent_stylesheet,
s,
can_gc,
)),
}
}

View file

@ -125,6 +125,11 @@ impl CSSRuleList {
let loader = owner
.as_ref()
.map(|element| StylesheetLoader::for_element(element));
let allow_import_rules = if self.parent_stylesheet.is_constructed() {
AllowImportRules::No
} else {
AllowImportRules::Yes
};
let new_rule = css_rules
.insert_rule(
&parent_stylesheet.shared_lock,
@ -134,7 +139,7 @@ impl CSSRuleList {
containing_rule_types,
parse_relative_rule_type,
loader.as_ref().map(|l| l as &dyn StyleStylesheetLoader),
AllowImportRules::Yes,
allow_import_rules,
)
.map_err(Convert::convert)?;
@ -190,20 +195,32 @@ impl CSSRuleList {
self.dom_rules.borrow().get(idx as usize).map(|rule| {
rule.or_init(|| {
let parent_stylesheet = &self.parent_stylesheet;
let guard = parent_stylesheet.shared_lock().read();
let lock = parent_stylesheet.shared_lock();
match self.rules {
RulesSource::Rules(ref rules) => CSSRule::new_specific(
self.global().as_window(),
parent_stylesheet,
rules.read_with(&guard).0[idx as usize].clone(),
can_gc,
),
RulesSource::Keyframes(ref rules) => DomRoot::upcast(CSSKeyframeRule::new(
self.global().as_window(),
parent_stylesheet,
rules.read_with(&guard).keyframes[idx as usize].clone(),
can_gc,
)),
RulesSource::Rules(ref rules) => {
let rule = {
let guard = lock.read();
rules.read_with(&guard).0[idx as usize].clone()
};
CSSRule::new_specific(
self.global().as_window(),
parent_stylesheet,
rule,
can_gc,
)
},
RulesSource::Keyframes(ref rules) => {
let rule = {
let guard = lock.read();
rules.read_with(&guard).keyframes[idx as usize].clone()
};
DomRoot::upcast(CSSKeyframeRule::new(
self.global().as_window(),
parent_stylesheet,
rule,
can_gc,
))
},
}
})
})

View file

@ -45,6 +45,9 @@ pub(crate) struct CSSStyleDeclaration {
#[derive(JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub(crate) enum CSSStyleOwner {
/// Used when calling `getComputedStyle()` with an invalid pseudo-element selector.
/// See <https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle>
Null,
Element(Dom<Element>),
CSSRule(
Dom<CSSRule>,
@ -66,6 +69,9 @@ impl CSSStyleOwner {
// This is somewhat complex but the complexity is encapsulated.
let mut changed = true;
match *self {
CSSStyleOwner::Null => unreachable!(
"CSSStyleDeclaration should always be read-only when CSSStyleOwner is Null"
),
CSSStyleOwner::Element(ref el) => {
let document = el.owner_document();
let shared_lock = document.style_shared_lock();
@ -135,6 +141,9 @@ impl CSSStyleOwner {
F: FnOnce(&PropertyDeclarationBlock) -> R,
{
match *self {
CSSStyleOwner::Null => {
unreachable!("Should never call with_block for CSStyleOwner::Null")
},
CSSStyleOwner::Element(ref el) => match *el.style_attribute().borrow() {
Some(ref pdb) => {
let document = el.owner_document();
@ -155,6 +164,9 @@ impl CSSStyleOwner {
fn window(&self) -> DomRoot<Window> {
match *self {
CSSStyleOwner::Null => {
unreachable!("Should never try to access window of CSStyleOwner::Null")
},
CSSStyleOwner::Element(ref el) => el.owner_window(),
CSSStyleOwner::CSSRule(ref rule, _) => DomRoot::from_ref(rule.global().as_window()),
}
@ -162,6 +174,9 @@ impl CSSStyleOwner {
fn base_url(&self) -> ServoUrl {
match *self {
CSSStyleOwner::Null => {
unreachable!("Should never try to access base URL of CSStyleOwner::Null")
},
CSSStyleOwner::Element(ref el) => el.owner_document().base_url(),
CSSStyleOwner::CSSRule(ref rule, _) => ServoUrl::from(
rule.parent_stylesheet()
@ -221,6 +236,13 @@ impl CSSStyleDeclaration {
pseudo: Option<PseudoElement>,
modification_access: CSSModificationAccess,
) -> CSSStyleDeclaration {
// If creating a CSSStyleDeclaration with CSSSStyleOwner::Null, this should always
// be in read-only mode.
assert!(
!matches!(owner, CSSStyleOwner::Null) ||
modification_access == CSSModificationAccess::Readonly
);
CSSStyleDeclaration {
reflector_: Reflector::new(),
owner,
@ -262,10 +284,15 @@ impl CSSStyleDeclaration {
node.owner_window()
.resolved_style_query(addr, self.pseudo, property, can_gc)
},
CSSStyleOwner::Null => DOMString::new(),
}
}
fn get_property_value(&self, id: PropertyId, can_gc: CanGc) -> DOMString {
if matches!(self.owner, CSSStyleOwner::Null) {
return DOMString::new();
}
if self.readonly {
// Readonly style declarations are used for getComputedStyle.
return self.get_computed_style(id, can_gc);
@ -388,6 +415,10 @@ pub(crate) static ENABLED_LONGHAND_PROPERTIES: LazyLock<Vec<LonghandId>> = LazyL
impl CSSStyleDeclarationMethods<crate::DomTypeHolder> for CSSStyleDeclaration {
// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-length
fn Length(&self) -> u32 {
if matches!(self.owner, CSSStyleOwner::Null) {
return 0;
}
if self.readonly {
// Readonly style declarations are used for getComputedStyle.
// TODO: include custom properties whose computed value is not the guaranteed-invalid value.
@ -489,6 +520,9 @@ impl CSSStyleDeclarationMethods<crate::DomTypeHolder> for CSSStyleDeclaration {
// https://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
fn IndexedGetter(&self, index: u32) -> Option<DOMString> {
if matches!(self.owner, CSSStyleOwner::Null) {
return None;
}
if self.readonly {
// Readonly style declarations are used for getComputedStyle.
// TODO: include custom properties whose computed value is not the guaranteed-invalid value.

View file

@ -10,14 +10,15 @@ use selectors::parser::{ParseRelative, SelectorList};
use servo_arc::Arc;
use style::selector_parser::SelectorParser;
use style::shared_lock::{Locked, ToCssWithGuard};
use style::stylesheets::{CssRuleType, Origin, StyleRule};
use style::stylesheets::{CssRuleType, CssRules, Origin, StyleRule};
use crate::dom::bindings::codegen::Bindings::CSSStyleRuleBinding::CSSStyleRuleMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::cssrule::{CSSRule, SpecificCSSRule};
use crate::dom::cssgroupingrule::CSSGroupingRule;
use crate::dom::cssrule::SpecificCSSRule;
use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
use crate::dom::cssstylesheet::CSSStyleSheet;
use crate::dom::node::NodeTraits;
@ -26,7 +27,7 @@ use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct CSSStyleRule {
cssrule: CSSRule,
cssgroupingrule: CSSGroupingRule,
#[ignore_malloc_size_of = "Arc"]
#[no_trace]
stylerule: Arc<Locked<StyleRule>>,
@ -39,7 +40,7 @@ impl CSSStyleRule {
stylerule: Arc<Locked<StyleRule>>,
) -> CSSStyleRule {
CSSStyleRule {
cssrule: CSSRule::new_inherited(parent_stylesheet),
cssgroupingrule: CSSGroupingRule::new_inherited(parent_stylesheet),
stylerule,
style_decl: Default::default(),
}
@ -58,6 +59,16 @@ impl CSSStyleRule {
can_gc,
)
}
pub(crate) fn ensure_rules(&self) -> Arc<Locked<CssRules>> {
let lock = self.cssgroupingrule.shared_lock();
let mut guard = lock.write();
self.stylerule
.write_with(&mut guard)
.rules
.get_or_insert_with(|| CssRules::new(vec![], lock))
.clone()
}
}
impl SpecificCSSRule for CSSStyleRule {
@ -66,7 +77,7 @@ impl SpecificCSSRule for CSSStyleRule {
}
fn get_css(&self) -> DOMString {
let guard = self.cssrule.shared_lock().read();
let guard = self.cssgroupingrule.shared_lock().read();
self.stylerule
.read_with(&guard)
.to_css_string(&guard)
@ -76,9 +87,9 @@ impl SpecificCSSRule for CSSStyleRule {
impl CSSStyleRuleMethods<crate::DomTypeHolder> for CSSStyleRule {
// https://drafts.csswg.org/cssom/#dom-cssstylerule-style
fn Style(&self) -> DomRoot<CSSStyleDeclaration> {
fn Style(&self, can_gc: CanGc) -> DomRoot<CSSStyleDeclaration> {
self.style_decl.or_init(|| {
let guard = self.cssrule.shared_lock().read();
let guard = self.cssgroupingrule.shared_lock().read();
CSSStyleDeclaration::new(
self.global().as_window(),
CSSStyleOwner::CSSRule(
@ -87,21 +98,25 @@ impl CSSStyleRuleMethods<crate::DomTypeHolder> for CSSStyleRule {
),
None,
CSSModificationAccess::ReadWrite,
CanGc::note(),
can_gc,
)
})
}
// https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext
fn SelectorText(&self) -> DOMString {
let guard = self.cssrule.shared_lock().read();
let guard = self.cssgroupingrule.shared_lock().read();
let stylerule = self.stylerule.read_with(&guard);
DOMString::from_string(stylerule.selectors.to_css_string())
}
// https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext
fn SetSelectorText(&self, value: DOMString) {
let contents = &self.cssrule.parent_stylesheet().style_stylesheet().contents;
let contents = &self
.cssgroupingrule
.parent_stylesheet()
.style_stylesheet()
.contents;
// It's not clear from the spec if we should use the stylesheet's namespaces.
// https://github.com/w3c/csswg-drafts/issues/1511
let namespaces = contents.namespaces.read();
@ -118,10 +133,10 @@ impl CSSStyleRuleMethods<crate::DomTypeHolder> for CSSStyleRule {
// rule?
if let Ok(mut s) = SelectorList::parse(&parser, &mut css_parser, ParseRelative::No) {
// This mirrors what we do in CSSStyleOwner::mutate_associated_block.
let mut guard = self.cssrule.shared_lock().write();
let mut guard = self.cssgroupingrule.shared_lock().write();
let stylerule = self.stylerule.write_with(&mut guard);
mem::swap(&mut stylerule.selectors, &mut s);
if let Some(owner) = self.cssrule.parent_stylesheet().get_owner() {
if let Some(owner) = self.cssgroupingrule.parent_stylesheet().get_owner() {
owner.stylesheet_list_owner().invalidate_stylesheets();
}
}

View file

@ -5,15 +5,26 @@
use std::cell::Cell;
use dom_struct::dom_struct;
use js::rust::HandleObject;
use servo_arc::Arc;
use style::media_queries::MediaList as StyleMediaList;
use style::shared_lock::SharedRwLock;
use style::stylesheets::{CssRuleTypes, Stylesheet as StyleStyleSheet};
use style::stylesheets::{
AllowImportRules, CssRuleTypes, Origin, Stylesheet as StyleStyleSheet, UrlExtraData,
};
use crate::dom::bindings::codegen::Bindings::CSSStyleSheetBinding::CSSStyleSheetMethods;
use crate::dom::bindings::codegen::Bindings::CSSStyleSheetBinding::{
CSSStyleSheetInit, CSSStyleSheetMethods,
};
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use crate::dom::bindings::codegen::GenericBindings::CSSRuleListBinding::CSSRuleList_Binding::CSSRuleListMethods;
use crate::dom::bindings::codegen::UnionTypes::MediaListOrString;
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::reflector::{
DomGlobal, reflect_dom_object, reflect_dom_object_with_proto,
};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::cssrulelist::{CSSRuleList, RulesSource};
use crate::dom::element::Element;
use crate::dom::medialist::MediaList;
@ -31,52 +42,90 @@ pub(crate) struct CSSStyleSheet {
#[no_trace]
style_stylesheet: Arc<StyleStyleSheet>,
origin_clean: Cell<bool>,
is_constructed: bool,
}
impl CSSStyleSheet {
fn new_inherited(
owner: &Element,
owner: Option<&Element>,
type_: DOMString,
href: Option<DOMString>,
title: Option<DOMString>,
stylesheet: Arc<StyleStyleSheet>,
is_constructed: bool,
) -> CSSStyleSheet {
CSSStyleSheet {
stylesheet: StyleSheet::new_inherited(type_, href, title),
owner: MutNullableDom::new(Some(owner)),
owner: MutNullableDom::new(owner),
rulelist: MutNullableDom::new(None),
style_stylesheet: stylesheet,
origin_clean: Cell::new(true),
is_constructed,
}
}
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
window: &Window,
owner: &Element,
owner: Option<&Element>,
type_: DOMString,
href: Option<DOMString>,
title: Option<DOMString>,
stylesheet: Arc<StyleStyleSheet>,
is_constructed: bool,
can_gc: CanGc,
) -> DomRoot<CSSStyleSheet> {
reflect_dom_object(
Box::new(CSSStyleSheet::new_inherited(
owner, type_, href, title, stylesheet,
owner,
type_,
href,
title,
stylesheet,
is_constructed,
)),
window,
can_gc,
)
}
fn rulelist(&self) -> DomRoot<CSSRuleList> {
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
#[allow(clippy::too_many_arguments)]
fn new_with_proto(
window: &Window,
proto: Option<HandleObject>,
owner: Option<&Element>,
type_: DOMString,
href: Option<DOMString>,
title: Option<DOMString>,
stylesheet: Arc<StyleStyleSheet>,
is_constructed: bool,
can_gc: CanGc,
) -> DomRoot<CSSStyleSheet> {
reflect_dom_object_with_proto(
Box::new(CSSStyleSheet::new_inherited(
owner,
type_,
href,
title,
stylesheet,
is_constructed,
)),
window,
proto,
can_gc,
)
}
fn rulelist(&self, can_gc: CanGc) -> DomRoot<CSSRuleList> {
self.rulelist.or_init(|| {
let rules = self.style_stylesheet.contents.rules.clone();
CSSRuleList::new(
self.global().as_window(),
self,
RulesSource::Rules(rules),
CanGc::note(),
can_gc,
)
})
}
@ -122,31 +171,151 @@ impl CSSStyleSheet {
can_gc,
)
}
/// <https://drafts.csswg.org/cssom/#concept-css-style-sheet-constructed-flag>
#[inline]
pub(crate) fn is_constructed(&self) -> bool {
self.is_constructed
}
}
impl CSSStyleSheetMethods<crate::DomTypeHolder> for CSSStyleSheet {
// https://drafts.csswg.org/cssom/#dom-cssstylesheet-cssrules
fn GetCssRules(&self) -> Fallible<DomRoot<CSSRuleList>> {
if !self.origin_clean.get() {
return Err(Error::Security);
/// <https://drafts.csswg.org/cssom/#dom-cssstylesheet-cssstylesheet>
fn Constructor(
window: &Window,
proto: Option<HandleObject>,
can_gc: CanGc,
options: &CSSStyleSheetInit,
) -> DomRoot<Self> {
let doc = window.Document();
let shared_lock = doc.style_shared_lock().clone();
let media = Arc::new(shared_lock.wrap(match &options.media {
Some(media) => match media {
MediaListOrString::MediaList(media_list) => media_list.clone_media_list(),
MediaListOrString::String(str) => MediaList::parse_media_list(str, window),
},
None => StyleMediaList::empty(),
}));
let stylesheet = Arc::new(StyleStyleSheet::from_str(
"",
UrlExtraData(window.get_url().get_arc()),
Origin::Author,
media,
shared_lock,
None,
window.css_error_reporter(),
doc.quirks_mode(),
AllowImportRules::No,
));
if options.disabled {
stylesheet.set_disabled(true);
}
Ok(self.rulelist())
Self::new_with_proto(
window,
proto,
None, // owner
"text/css".into(),
None, // href
None, // title
stylesheet,
true, // is_constructed
can_gc,
)
}
// https://drafts.csswg.org/cssom/#dom-cssstylesheet-insertrule
fn InsertRule(&self, rule: DOMString, index: u32) -> Fallible<u32> {
/// <https://drafts.csswg.org/cssom/#dom-cssstylesheet-cssrules>
fn GetCssRules(&self, can_gc: CanGc) -> Fallible<DomRoot<CSSRuleList>> {
if !self.origin_clean.get() {
return Err(Error::Security);
}
self.rulelist()
.insert_rule(&rule, index, CssRuleTypes::default(), None, CanGc::note())
Ok(self.rulelist(can_gc))
}
// https://drafts.csswg.org/cssom/#dom-cssstylesheet-deleterule
fn DeleteRule(&self, index: u32) -> ErrorResult {
/// <https://drafts.csswg.org/cssom/#dom-cssstylesheet-insertrule>
fn InsertRule(&self, rule: DOMString, index: u32, can_gc: CanGc) -> Fallible<u32> {
if !self.origin_clean.get() {
return Err(Error::Security);
}
self.rulelist().remove_rule(index)
self.rulelist(can_gc)
.insert_rule(&rule, index, CssRuleTypes::default(), None, can_gc)
}
/// <https://drafts.csswg.org/cssom/#dom-cssstylesheet-deleterule>
fn DeleteRule(&self, index: u32, can_gc: CanGc) -> ErrorResult {
if !self.origin_clean.get() {
return Err(Error::Security);
}
self.rulelist(can_gc).remove_rule(index)
}
/// <https://drafts.csswg.org/cssom/#dom-cssstylesheet-rules>
fn GetRules(&self, can_gc: CanGc) -> Fallible<DomRoot<CSSRuleList>> {
self.GetCssRules(can_gc)
}
/// <https://drafts.csswg.org/cssom/#dom-cssstylesheet-removerule>
fn RemoveRule(&self, index: u32, can_gc: CanGc) -> ErrorResult {
self.DeleteRule(index, can_gc)
}
/// <https://drafts.csswg.org/cssom/#dom-cssstylesheet-addrule>
fn AddRule(
&self,
selector: DOMString,
block: DOMString,
optional_index: Option<u32>,
can_gc: CanGc,
) -> Fallible<i32> {
// > 1. Let *rule* be an empty string.
// > 2. Append *selector* to *rule*.
let mut rule = selector;
// > 3. Append " { " to *rule*.
// > 4. If *block* is not empty, append *block*, followed by a space, to *rule*.
// > 5. Append "}" to *rule*.
if block.is_empty() {
rule.push_str(" { }");
} else {
rule.push_str(" { ");
rule.push_str(block.str());
rule.push_str(" }");
};
// > 6. Let *index* be *optionalIndex* if provided, or the number of CSS rules in the stylesheet otherwise.
let index = optional_index.unwrap_or_else(|| self.rulelist(can_gc).Length());
// > 7. Call `insertRule()`, with *rule* and *index* as arguments.
self.InsertRule(rule, index, can_gc)?;
// > 8. Return -1.
Ok(-1)
}
/// <https://drafts.csswg.org/cssom/#synchronously-replace-the-rules-of-a-cssstylesheet>
fn ReplaceSync(&self, text: USVString) -> Result<(), Error> {
// Step 1. If the constructed flag is not set throw a NotAllowedError
if !self.is_constructed {
return Err(Error::NotAllowed);
}
// Step 2. Let rules be the result of running parse a stylesheets contents from text.
let global = self.global();
let window = global.as_window();
StyleStyleSheet::update_from_str(
&self.style_stylesheet,
&text,
UrlExtraData(window.get_url().get_arc()),
None,
window.css_error_reporter(),
AllowImportRules::No, // Step 3.If rules contains one or more @import rules, remove those rules from rules.
);
// Step 4. Set sheets CSS rules to rules.
// We reset our rule list, which will be initialized properly
// at the next getter access.
self.rulelist.set(None);
Ok(())
}
}

View file

@ -9,7 +9,7 @@ use std::rc::Rc;
use std::{mem, ptr};
use dom_struct::dom_struct;
use html5ever::{LocalName, Namespace, Prefix, namespace_url, ns};
use html5ever::{LocalName, Namespace, Prefix, ns};
use js::conversions::ToJSValConvertible;
use js::glue::UnwrapObjectStatic;
use js::jsapi::{HandleValueArray, Heap, IsCallable, IsConstructor, JSAutoRealm, JSObject};
@ -878,7 +878,7 @@ pub(crate) fn upgrade_element(
// We know this element is is form-associated, so we can use the implementation of
// `FormControl` for HTMLElement, which makes that assumption.
// Step 9.1: Reset the form owner of element
html_element.reset_form_owner();
html_element.reset_form_owner(can_gc);
if let Some(form) = html_element.form_owner() {
// Even though the tree hasn't structurally mutated,
// HTMLCollections need to be invalidated.

View file

@ -93,7 +93,7 @@ impl CustomEventMethods<crate::DomTypeHolder> for CustomEvent {
type_: DOMString,
init: RootedTraceableBox<CustomEventBinding::CustomEventInit>,
) -> DomRoot<CustomEvent> {
CustomEvent::new(
let event = CustomEvent::new(
global,
proto,
Atom::from(type_),
@ -101,7 +101,9 @@ impl CustomEventMethods<crate::DomTypeHolder> for CustomEvent {
init.parent.cancelable,
init.detail.handle(),
can_gc,
)
);
event.upcast::<Event>().set_composed(init.parent.composed);
event
}
// https://dom.spec.whatwg.org/#dom-customevent-detail

View file

@ -7,6 +7,7 @@ use std::sync::atomic::AtomicBool;
use std::thread::{self, JoinHandle};
use base::id::{BrowsingContextId, PipelineId, WebViewId};
use constellation_traits::{WorkerGlobalScopeInit, WorkerScriptLoadOrigin};
use crossbeam_channel::{Receiver, Sender, unbounded};
use devtools_traits::DevtoolScriptControlMsg;
use dom_struct::dom_struct;
@ -17,11 +18,11 @@ use js::jsval::UndefinedValue;
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use net_traits::IpcSend;
use net_traits::image_cache::ImageCache;
use net_traits::policy_container::PolicyContainer;
use net_traits::request::{
CredentialsMode, Destination, InsecureRequestsPolicy, ParserMetadata, Referrer, RequestBuilder,
RequestMode,
};
use script_traits::{WorkerGlobalScopeInit, WorkerScriptLoadOrigin};
use servo_rand::random;
use servo_url::{ImmutableOrigin, ServoUrl};
use style::thread_state::{self, ThreadState};
@ -41,6 +42,7 @@ use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots}
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::{CustomTraceable, RootedTraceableBox};
use crate::dom::bindings::utils::define_all_exposed_interfaces;
use crate::dom::errorevent::ErrorEvent;
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventtarget::EventTarget;
@ -347,6 +349,7 @@ impl DedicatedWorkerGlobalScope {
control_receiver: Receiver<DedicatedWorkerControlMsg>,
context_sender: Sender<ThreadSafeJSContext>,
insecure_requests_policy: InsecureRequestsPolicy,
policy_container: PolicyContainer,
) -> JoinHandle<()> {
let serialized_worker_url = worker_url.to_string();
let webview_id = WebViewId::installed();
@ -355,6 +358,8 @@ impl DedicatedWorkerGlobalScope {
let referrer = current_global.get_referrer();
let parent = current_global.runtime_handle();
let current_global_https_state = current_global.get_https_state();
let current_global_ancestor_trustworthy = current_global.has_trustworthy_ancestor_origin();
let is_secure_context = current_global.is_secure_context();
thread::Builder::new()
.name(format!("WW:{}", worker_url.debug_compact()))
@ -384,8 +389,9 @@ impl DedicatedWorkerGlobalScope {
.use_url_credentials(true)
.pipeline_id(Some(pipeline_id))
.referrer_policy(referrer_policy)
.referrer_policy(referrer_policy)
.insecure_requests_policy(insecure_requests_policy)
.has_trustworthy_ancestor_origin(current_global_ancestor_trustworthy)
.policy_container(policy_container)
.origin(origin);
let runtime = unsafe {
@ -418,7 +424,12 @@ impl DedicatedWorkerGlobalScope {
// > scope`'s url's scheme is "data", and `inherited origin`
// > otherwise.
if worker_url.scheme() == "data" {
init.origin = ImmutableOrigin::new_opaque();
// Workers created from a data: url are secure if they were created from secure contexts
if is_secure_context {
init.origin = ImmutableOrigin::new_opaque_data_url_worker();
} else {
init.origin = ImmutableOrigin::new_opaque();
}
}
let global = DedicatedWorkerGlobalScope::new(
@ -468,6 +479,7 @@ impl DedicatedWorkerGlobalScope {
Ok((metadata, bytes)) => (metadata, bytes),
};
scope.set_url(metadata.final_url);
scope.set_csp_list(GlobalScope::parse_csp_list_from_metadata(&metadata.headers));
global_scope.set_https_state(metadata.https_state);
let source = String::from_utf8_lossy(&bytes);
@ -483,7 +495,12 @@ impl DedicatedWorkerGlobalScope {
{
let _ar = AutoWorkerReset::new(&global, worker.clone());
let _ac = enter_realm(scope);
let realm = enter_realm(scope);
define_all_exposed_interfaces(
global.upcast(),
InRealm::entered(&realm),
CanGc::note(),
);
scope.execute_script(DOMString::from(source), CanGc::note());
}

View file

@ -21,7 +21,7 @@ use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::readablestream::ReadableStream;
use crate::microtask::Microtask;
use crate::script_runtime::CanGc;
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
#[derive(JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
@ -32,8 +32,8 @@ pub(crate) struct DefaultTeeReadRequestMicrotask {
}
impl DefaultTeeReadRequestMicrotask {
pub(crate) fn microtask_chunk_steps(&self, can_gc: CanGc) {
self.tee_read_request.chunk_steps(&self.chunk, can_gc)
pub(crate) fn microtask_chunk_steps(&self, cx: SafeJSContext, can_gc: CanGc) {
self.tee_read_request.chunk_steps(cx, &self.chunk, can_gc)
}
}
@ -94,8 +94,14 @@ impl DefaultTeeReadRequest {
}
/// Call into cancel of the stream,
/// <https://streams.spec.whatwg.org/#readable-stream-cancel>
pub(crate) fn stream_cancel(&self, reason: SafeHandleValue, can_gc: CanGc) {
self.stream.cancel(reason, can_gc);
pub(crate) fn stream_cancel(
&self,
cx: SafeJSContext,
global: &GlobalScope,
reason: SafeHandleValue,
can_gc: CanGc,
) {
self.stream.cancel(cx, global, reason, can_gc);
}
/// Enqueue a microtask to perform the chunk steps
/// <https://streams.spec.whatwg.org/#ref-for-read-request-chunk-steps%E2%91%A2>
@ -115,13 +121,13 @@ impl DefaultTeeReadRequest {
}
/// <https://streams.spec.whatwg.org/#ref-for-read-request-chunk-steps%E2%91%A2>
#[allow(clippy::borrowed_box)]
pub(crate) fn chunk_steps(&self, chunk: &Box<Heap<JSVal>>, can_gc: CanGc) {
pub(crate) fn chunk_steps(&self, cx: SafeJSContext, chunk: &Box<Heap<JSVal>>, can_gc: CanGc) {
let global = &self.stream.global();
// Set readAgain to false.
self.read_again.set(false);
// Let chunk1 and chunk2 be chunk.
let chunk1 = chunk;
let chunk2 = chunk;
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let chunk1_value = chunk1.get());
rooted!(in(*cx) let chunk2_value = chunk2.get());
@ -131,9 +137,7 @@ impl DefaultTeeReadRequest {
rooted!(in(*cx) let mut clone_result = UndefinedValue());
let data = structuredclone::write(cx, chunk2_value.handle(), None).unwrap();
// If cloneResult is an abrupt completion,
if structuredclone::read(&self.stream.global(), data, clone_result.handle_mut())
.is_err()
{
if structuredclone::read(global, data, clone_result.handle_mut()).is_err() {
// Perform ! ReadableStreamDefaultControllerError(branch_1.[[controller]], cloneResult.[[Value]]).
self.readable_stream_default_controller_error(
&self.branch_1,
@ -148,7 +152,7 @@ impl DefaultTeeReadRequest {
can_gc,
);
// Resolve cancelPromise with ! ReadableStreamCancel(stream, cloneResult.[[Value]]).
self.stream_cancel(clone_result.handle(), can_gc);
self.stream_cancel(cx, global, clone_result.handle(), can_gc);
// Return.
return;
} else {

View file

@ -19,7 +19,7 @@ use crate::dom::defaultteereadrequest::DefaultTeeReadRequest;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::readablestreamdefaultreader::ReadRequest;
use crate::script_runtime::CanGc;
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
#[derive(JSTraceable, MallocSizeOf)]
pub(crate) enum TeeCancelAlgorithm {
@ -156,6 +156,8 @@ impl DefaultTeeUnderlyingSource {
#[allow(unsafe_code)]
pub(crate) fn cancel_algorithm(
&self,
cx: SafeJSContext,
global: &GlobalScope,
reason: SafeHandleValue,
can_gc: CanGc,
) -> Option<Result<Rc<Promise>, Error>> {
@ -169,7 +171,7 @@ impl DefaultTeeUnderlyingSource {
// If canceled_2 is true,
if self.canceled_2.get() {
self.resolve_cancel_promise(can_gc);
self.resolve_cancel_promise(cx, global, can_gc);
}
// Return cancelPromise.
Some(Ok(self.cancel_promise.clone()))
@ -183,7 +185,7 @@ impl DefaultTeeUnderlyingSource {
// If canceled_1 is true,
if self.canceled_1.get() {
self.resolve_cancel_promise(can_gc);
self.resolve_cancel_promise(cx, global, can_gc);
}
// Return cancelPromise.
Some(Ok(self.cancel_promise.clone()))
@ -192,9 +194,8 @@ impl DefaultTeeUnderlyingSource {
}
#[allow(unsafe_code)]
fn resolve_cancel_promise(&self, can_gc: CanGc) {
fn resolve_cancel_promise(&self, cx: SafeJSContext, global: &GlobalScope, can_gc: CanGc) {
// Let compositeReason be ! CreateArrayFromList(« reason_1, reason_2 »).
let cx = GlobalScope::get_cx();
rooted_vec!(let mut reasons_values);
reasons_values.push(self.reason_1.get());
reasons_values.push(self.reason_2.get());
@ -204,7 +205,9 @@ impl DefaultTeeUnderlyingSource {
rooted!(in(*cx) let reasons_value = ObjectValue(reasons.get()));
// Let cancelResult be ! ReadableStreamCancel(stream, compositeReason).
let cancel_result = self.stream.cancel(reasons_value.handle(), can_gc);
let cancel_result = self
.stream
.cancel(cx, global, reasons_value.handle(), can_gc);
// Resolve cancelPromise with cancelResult.
self.cancel_promise.resolve_native(&cancel_result, can_gc);

View file

@ -3,11 +3,11 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use base::id::PipelineId;
use constellation_traits::{ScriptToConstellationMessage, StructuredSerializedData};
use dom_struct::dom_struct;
use js::jsapi::{Heap, JSObject};
use js::jsval::UndefinedValue;
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue, MutableHandleValue};
use script_traits::{ScriptMsg, StructuredSerializedData};
use servo_url::ServoUrl;
use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding;
@ -64,7 +64,6 @@ impl DissimilarOriginWindow {
// FIXME(nox): The microtask queue is probably not important
// here, but this whole DOM interface is a hack anyway.
global_to_clone_from.microtask_queue().clone(),
global_to_clone_from.get_user_agent(),
#[cfg(feature = "webgpu")]
global_to_clone_from.wgpu_id_hub(),
Some(global_to_clone_from.is_secure_context()),
@ -182,18 +181,19 @@ impl DissimilarOriginWindowMethods<crate::DomTypeHolder> for DissimilarOriginWin
// https://html.spec.whatwg.org/multipage/#dom-window-blur
fn Blur(&self) {
// TODO: Implement x-origin blur
// > User agents are encouraged to ignore calls to this `blur()` method
// > entirely.
}
// https://html.spec.whatwg.org/multipage/#dom-focus
// https://html.spec.whatwg.org/multipage/#dom-window-focus
fn Focus(&self) {
// TODO: Implement x-origin focus
self.window_proxy().focus();
}
// https://html.spec.whatwg.org/multipage/#dom-location
fn Location(&self) -> DomRoot<DissimilarOriginLocation> {
fn Location(&self, can_gc: CanGc) -> DomRoot<DissimilarOriginLocation> {
self.location
.or_init(|| DissimilarOriginLocation::new(self, CanGc::note()))
.or_init(|| DissimilarOriginLocation::new(self, can_gc))
}
}
@ -237,7 +237,7 @@ impl DissimilarOriginWindow {
Err(_) => return Err(Error::Syntax),
},
};
let msg = ScriptMsg::PostMessage {
let msg = ScriptToConstellationMessage::PostMessage {
target,
source: incumbent.pipeline_id(),
source_origin,

File diff suppressed because it is too large Load diff

View file

@ -76,9 +76,9 @@ impl DocumentFragmentMethods<crate::DomTypeHolder> for DocumentFragment {
}
// https://dom.spec.whatwg.org/#dom-parentnode-children
fn Children(&self) -> DomRoot<HTMLCollection> {
fn Children(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
let window = self.owner_window();
HTMLCollection::children(&window, self.upcast())
HTMLCollection::children(&window, self.upcast(), can_gc)
}
// https://dom.spec.whatwg.org/#dom-nonelementparentnode-getelementbyid

View file

@ -4,7 +4,7 @@
use std::fmt;
use constellation_traits::UntrustedNodeAddress;
use embedder_traits::UntrustedNodeAddress;
use euclid::default::Point2D;
use script_layout_interface::{NodesFromPointQueryType, QueryMsg};
use servo_arc::Arc;
@ -128,7 +128,7 @@ impl DocumentOrShadowRoot {
let x = *x as f32;
let y = *y as f32;
let point = &Point2D::new(x, y);
let viewport = self.window.window_size().initial_viewport;
let viewport = self.window.viewport_details().size;
if !has_browsing_context {
return None;
@ -176,7 +176,7 @@ impl DocumentOrShadowRoot {
let x = *x as f32;
let y = *y as f32;
let point = &Point2D::new(x, y);
let viewport = self.window.window_size().initial_viewport;
let viewport = self.window.viewport_details().size;
if !has_browsing_context {
return vec![];

View file

@ -103,7 +103,7 @@ impl DocumentTypeMethods<crate::DomTypeHolder> for DocumentType {
}
// https://dom.spec.whatwg.org/#dom-childnode-remove
fn Remove(&self) {
self.upcast::<Node>().remove_self();
fn Remove(&self, can_gc: CanGc) {
self.upcast::<Node>().remove_self(can_gc);
}
}

View file

@ -2,6 +2,10 @@
* 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 std::collections::HashMap;
use base::id::{DomExceptionId, DomExceptionIndex};
use constellation_traits::DomException;
use dom_struct::dom_struct;
use js::rust::HandleObject;
@ -13,7 +17,9 @@ use crate::dom::bindings::reflector::{
Reflector, reflect_dom_object, reflect_dom_object_with_proto,
};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::serializable::Serializable;
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone::StructuredData;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::CanGc;
@ -47,6 +53,7 @@ pub(crate) enum DOMErrorName {
NotReadableError,
DataError,
OperationError,
NotAllowedError,
}
impl DOMErrorName {
@ -78,6 +85,7 @@ impl DOMErrorName {
"NotReadableError" => Some(DOMErrorName::NotReadableError),
"DataError" => Some(DOMErrorName::DataError),
"OperationError" => Some(DOMErrorName::OperationError),
"NotAllowedError" => Some(DOMErrorName::NotAllowedError),
_ => None,
}
}
@ -129,6 +137,10 @@ impl DOMException {
DOMErrorName::OperationError => {
"The operation failed for an operation-specific reason."
},
DOMErrorName::NotAllowedError => {
r#"The request is not allowed by the user agent or the platform in the current context,
possibly because the user denied permission."#
},
};
(
@ -159,6 +171,21 @@ impl DOMException {
)
}
pub(crate) fn new_with_custom_message(
global: &GlobalScope,
code: DOMErrorName,
message: String,
can_gc: CanGc,
) -> DomRoot<DOMException> {
let (_, name) = DOMException::get_error_data_by_code(code);
reflect_dom_object(
Box::new(DOMException::new_inherited(DOMString::from(message), name)),
global,
can_gc,
)
}
// not an IDL stringifier, used internally
pub(crate) fn stringifier(&self) -> DOMString {
DOMString::from(format!("{}: {}", self.name, self.message))
@ -200,3 +227,43 @@ impl DOMExceptionMethods<crate::DomTypeHolder> for DOMException {
self.message.clone()
}
}
impl Serializable for DOMException {
type Index = DomExceptionIndex;
type Data = DomException;
// https://webidl.spec.whatwg.org/#idl-DOMException
fn serialize(&self) -> Result<(DomExceptionId, Self::Data), ()> {
let serialized = DomException {
message: self.message.to_string(),
name: self.name.to_string(),
};
Ok((DomExceptionId::new(), serialized))
}
// https://webidl.spec.whatwg.org/#idl-DOMException
fn deserialize(
owner: &GlobalScope,
serialized: Self::Data,
can_gc: CanGc,
) -> Result<DomRoot<Self>, ()>
where
Self: Sized,
{
Ok(Self::new_with_custom_message(
owner,
DOMErrorName::from(&DOMString::from_string(serialized.name)).ok_or(())?,
serialized.message,
can_gc,
))
}
fn serialized_storage<'a>(
data: StructuredData<'a, '_>,
) -> &'a mut Option<HashMap<DomExceptionId, Self::Data>> {
match data {
StructuredData::Reader(reader) => &mut reader.exceptions,
StructuredData::Writer(writer) => &mut writer.exceptions,
}
}
}

View file

@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use html5ever::{local_name, namespace_url, ns};
use html5ever::{local_name, ns};
use script_traits::DocumentActivity;
use crate::document_loader::DocumentLoader;
@ -92,10 +92,12 @@ impl DOMImplementationMethods<crate::DomTypeHolder> for DOMImplementation {
let namespace = namespace_from_domstring(maybe_namespace.to_owned());
let content_type = match namespace {
ns!(html) => "application/xhtml+xml".parse().unwrap(),
ns!(svg) => mime::IMAGE_SVG,
_ => "application/xml".parse().unwrap(),
};
ns!(html) => "application/xhtml+xml",
ns!(svg) => "image/svg+xml",
_ => "application/xml",
}
.parse()
.unwrap();
// Step 1.
let doc = XMLDocument::new(
@ -110,6 +112,7 @@ impl DOMImplementationMethods<crate::DomTypeHolder> for DOMImplementation {
DocumentSource::NotFromParser,
loader,
Some(self.document.insecure_requests_policy()),
self.document.has_trustworthy_ancestor_or_current_origin(),
can_gc,
);
@ -137,12 +140,12 @@ impl DOMImplementationMethods<crate::DomTypeHolder> for DOMImplementation {
// Step 4.
if let Some(doc_type) = maybe_doctype {
doc_node.AppendChild(doc_type.upcast()).unwrap();
doc_node.AppendChild(doc_type.upcast(), can_gc).unwrap();
}
// Step 5.
if let Some(ref elem) = maybe_elem {
doc_node.AppendChild(elem.upcast()).unwrap();
doc_node.AppendChild(elem.upcast(), can_gc).unwrap();
}
}
@ -176,6 +179,7 @@ impl DOMImplementationMethods<crate::DomTypeHolder> for DOMImplementation {
false,
self.document.allow_declarative_shadow_roots(),
Some(self.document.insecure_requests_policy()),
self.document.has_trustworthy_ancestor_or_current_origin(),
can_gc,
);
@ -183,7 +187,7 @@ impl DOMImplementationMethods<crate::DomTypeHolder> for DOMImplementation {
// Step 3.
let doc_node = doc.upcast::<Node>();
let doc_type = DocumentType::new(DOMString::from("html"), None, None, &doc, can_gc);
doc_node.AppendChild(doc_type.upcast()).unwrap();
doc_node.AppendChild(doc_type.upcast(), can_gc).unwrap();
}
{
@ -196,7 +200,9 @@ impl DOMImplementationMethods<crate::DomTypeHolder> for DOMImplementation {
None,
can_gc,
));
doc_node.AppendChild(&doc_html).expect("Appending failed");
doc_node
.AppendChild(&doc_html, can_gc)
.expect("Appending failed");
{
// Step 5.
@ -207,7 +213,7 @@ impl DOMImplementationMethods<crate::DomTypeHolder> for DOMImplementation {
None,
can_gc,
));
doc_html.AppendChild(&doc_head).unwrap();
doc_html.AppendChild(&doc_head, can_gc).unwrap();
// Step 6.
if let Some(title_str) = title {
@ -219,17 +225,17 @@ impl DOMImplementationMethods<crate::DomTypeHolder> for DOMImplementation {
None,
can_gc,
));
doc_head.AppendChild(&doc_title).unwrap();
doc_head.AppendChild(&doc_title, can_gc).unwrap();
// Step 6.2.
let title_text = Text::new(title_str, &doc, can_gc);
doc_title.AppendChild(title_text.upcast()).unwrap();
doc_title.AppendChild(title_text.upcast(), can_gc).unwrap();
}
}
// Step 7.
let doc_body = HTMLBodyElement::new(local_name!("body"), None, &doc, None, can_gc);
doc_html.AppendChild(doc_body.upcast()).unwrap();
doc_html.AppendChild(doc_body.upcast(), can_gc).unwrap();
}
// Step 8.

View file

@ -90,6 +90,7 @@ impl DOMParserMethods<crate::DomTypeHolder> for DOMParser {
false,
false,
Some(doc.insecure_requests_policy()),
doc.has_trustworthy_ancestor_or_current_origin(),
can_gc,
);
ServoParser::parse_html_document(&document, Some(s), url, can_gc);
@ -114,6 +115,7 @@ impl DOMParserMethods<crate::DomTypeHolder> for DOMParser {
false,
false,
Some(doc.insecure_requests_policy()),
doc.has_trustworthy_ancestor_or_current_origin(),
can_gc,
);
ServoParser::parse_xml_document(&document, Some(s), url, can_gc);

View file

@ -4,18 +4,18 @@
use std::collections::HashMap;
use base::id::DomPointId;
use base::id::{DomPointId, DomPointIndex};
use constellation_traits::DomPoint;
use dom_struct::dom_struct;
use js::rust::HandleObject;
use script_traits::serializable::DomPoint;
use crate::dom::bindings::codegen::Bindings::DOMPointBinding::{DOMPointInit, DOMPointMethods};
use crate::dom::bindings::codegen::Bindings::DOMPointReadOnlyBinding::DOMPointReadOnlyMethods;
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::serializable::{Serializable, StorageKey};
use crate::dom::bindings::structuredclone::{StructuredData, StructuredDataReader};
use crate::dom::bindings::serializable::Serializable;
use crate::dom::bindings::structuredclone::StructuredData;
use crate::dom::dompointreadonly::{DOMPointReadOnly, DOMPointWriteMethods};
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::CanGc;
@ -132,10 +132,10 @@ impl DOMPointMethods<crate::DomTypeHolder> for DOMPoint {
}
impl Serializable for DOMPoint {
type Id = DomPointId;
type Index = DomPointIndex;
type Data = DomPoint;
fn serialize(&self) -> Result<(Self::Id, Self::Data), ()> {
fn serialize(&self) -> Result<(DomPointId, Self::Data), ()> {
let serialized = DomPoint {
x: self.X(),
y: self.Y(),
@ -163,16 +163,12 @@ impl Serializable for DOMPoint {
))
}
fn serialized_storage(data: StructuredData<'_>) -> &mut Option<HashMap<Self::Id, Self::Data>> {
fn serialized_storage<'a>(
data: StructuredData<'a, '_>,
) -> &'a mut Option<HashMap<DomPointId, Self::Data>> {
match data {
StructuredData::Reader(reader) => &mut reader.points,
StructuredData::Writer(writer) => &mut writer.points,
}
}
fn deserialized_storage(
reader: &mut StructuredDataReader,
) -> &mut Option<HashMap<StorageKey, DomRoot<Self>>> {
&mut reader.dom_points
}
}

View file

@ -4,20 +4,19 @@
use std::cell::Cell;
use std::collections::HashMap;
use std::num::NonZeroU32;
use base::id::{DomPointId, DomPointIndex, PipelineNamespaceId};
use base::id::{DomPointId, DomPointIndex};
use constellation_traits::DomPoint;
use dom_struct::dom_struct;
use js::rust::HandleObject;
use script_traits::serializable::DomPoint;
use crate::dom::bindings::codegen::Bindings::DOMPointBinding::DOMPointInit;
use crate::dom::bindings::codegen::Bindings::DOMPointReadOnlyBinding::DOMPointReadOnlyMethods;
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::serializable::{IntoStorageKey, Serializable, StorageKey};
use crate::dom::bindings::structuredclone::{StructuredData, StructuredDataReader};
use crate::dom::bindings::serializable::Serializable;
use crate::dom::bindings::structuredclone::StructuredData;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::CanGc;
@ -142,10 +141,10 @@ impl DOMPointWriteMethods for DOMPointReadOnly {
}
impl Serializable for DOMPointReadOnly {
type Id = DomPointId;
type Index = DomPointIndex;
type Data = DomPoint;
fn serialize(&self) -> Result<(Self::Id, Self::Data), ()> {
fn serialize(&self) -> Result<(DomPointId, Self::Data), ()> {
let serialized = DomPoint {
x: self.x.get(),
y: self.y.get(),
@ -173,37 +172,12 @@ impl Serializable for DOMPointReadOnly {
))
}
fn serialized_storage(data: StructuredData<'_>) -> &mut Option<HashMap<Self::Id, Self::Data>> {
fn serialized_storage<'a>(
data: StructuredData<'a, '_>,
) -> &'a mut Option<HashMap<DomPointId, Self::Data>> {
match data {
StructuredData::Reader(r) => &mut r.points,
StructuredData::Writer(w) => &mut w.points,
}
}
fn deserialized_storage(
reader: &mut StructuredDataReader,
) -> &mut Option<HashMap<StorageKey, DomRoot<Self>>> {
&mut reader.points_read_only
}
}
impl From<StorageKey> for DomPointId {
fn from(storage_key: StorageKey) -> DomPointId {
let namespace_id = PipelineNamespaceId(storage_key.name_space);
let index = DomPointIndex(
NonZeroU32::new(storage_key.index).expect("Deserialized point index is zero"),
);
DomPointId {
namespace_id,
index,
}
}
}
impl IntoStorageKey for DomPointId {
fn into_storage_key(self) -> StorageKey {
let DomPointIndex(index) = self.index;
StorageKey::new(self.namespace_id, index)
}
}

View file

@ -75,10 +75,10 @@ impl DOMRectMethods<crate::DomTypeHolder> for DOMRect {
// https://drafts.fxtf.org/geometry/#dom-domrect-fromrect
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
fn FromRect(global: &GlobalScope, other: &DOMRectInit) -> DomRoot<DOMRect> {
fn FromRect(global: &GlobalScope, other: &DOMRectInit, can_gc: CanGc) -> DomRoot<DOMRect> {
let rect = create_a_domrectreadonly_from_the_dictionary(other);
reflect_dom_object(Box::new(Self { rect }), global, CanGc::note())
reflect_dom_object(Box::new(Self { rect }), global, can_gc)
}
// https://drafts.fxtf.org/geometry/#dom-domrect-x

View file

@ -5,7 +5,7 @@ use dom_struct::dom_struct;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::DOMRectListBinding::DOMRectListMethods;
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::domrect::DOMRect;
use crate::dom::window::Window;
@ -37,7 +37,7 @@ impl DOMRectList {
) -> DomRoot<DOMRectList> {
reflect_dom_object_with_proto(
Box::new(DOMRectList::new_inherited(rects)),
&*window.global(),
window,
None,
can_gc,
)

View file

@ -104,10 +104,14 @@ impl DOMRectReadOnlyMethods<crate::DomTypeHolder> for DOMRectReadOnly {
// https://drafts.fxtf.org/geometry/#dom-domrectreadonly-fromrect
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
fn FromRect(global: &GlobalScope, other: &DOMRectInit) -> DomRoot<DOMRectReadOnly> {
fn FromRect(
global: &GlobalScope,
other: &DOMRectInit,
can_gc: CanGc,
) -> DomRoot<DOMRectReadOnly> {
let dom_rect = create_a_domrectreadonly_from_the_dictionary(other);
reflect_dom_object(Box::new(dom_rect), global, CanGc::note())
reflect_dom_object(Box::new(dom_rect), global, can_gc)
}
// https://drafts.fxtf.org/geometry/#dom-domrectreadonly-x

View file

@ -39,8 +39,8 @@ impl DOMStringMap {
// https://html.spec.whatwg.org/multipage/#domstringmap
impl DOMStringMapMethods<crate::DomTypeHolder> for DOMStringMap {
// https://html.spec.whatwg.org/multipage/#dom-domstringmap-removeitem
fn NamedDeleter(&self, name: DOMString) {
self.element.delete_custom_attr(name)
fn NamedDeleter(&self, name: DOMString, can_gc: CanGc) {
self.element.delete_custom_attr(name, can_gc)
}
// https://html.spec.whatwg.org/multipage/#dom-domstringmap-setitem

View file

@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use html5ever::{LocalName, namespace_url, ns};
use html5ever::{LocalName, ns};
use style::str::HTML_SPACE_CHARACTERS;
use stylo_atoms::Atom;

File diff suppressed because it is too large Load diff

View file

@ -234,6 +234,7 @@ impl ElementInternalsMethods<crate::DomTypeHolder> for ElementInternals {
flags: &ValidityStateFlags,
message: Option<DOMString>,
anchor: Option<&HTMLElement>,
can_gc: CanGc,
) -> ErrorResult {
// Steps 1-2: Check form-associated custom element
if !self.is_target_form_associated() {
@ -253,7 +254,7 @@ impl ElementInternalsMethods<crate::DomTypeHolder> for ElementInternals {
// Step 4: For each entry `flag` → `value` of `flags`, set element's validity flag with the name
// `flag` to `value`.
self.validity_state().update_invalid_flags(bits);
self.validity_state().update_pseudo_classes();
self.validity_state().update_pseudo_classes(can_gc);
// Step 5: Set element's validation message to the empty string if message is not given
// or all of element's validity flags are false, or to message otherwise.
@ -309,7 +310,7 @@ impl ElementInternalsMethods<crate::DomTypeHolder> for ElementInternals {
}
/// <https://html.spec.whatwg.org/multipage#dom-elementinternals-labels>
fn GetLabels(&self) -> Fallible<DomRoot<NodeList>> {
fn GetLabels(&self, can_gc: CanGc) -> Fallible<DomRoot<NodeList>> {
if !self.is_target_form_associated() {
return Err(Error::NotSupported);
}
@ -317,7 +318,7 @@ impl ElementInternalsMethods<crate::DomTypeHolder> for ElementInternals {
NodeList::new_labels_list(
self.target_element.upcast::<Node>().owner_doc().window(),
&self.target_element,
CanGc::note(),
can_gc,
)
}))
}

View file

@ -142,6 +142,7 @@ impl ErrorEventMethods<crate::DomTypeHolder> for ErrorEvent {
init.error.handle(),
can_gc,
);
event.upcast::<Event>().set_composed(init.parent.composed);
Ok(event)
}

View file

@ -29,7 +29,7 @@ use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_w
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::element::Element;
use crate::dom::eventtarget::{CompiledEventListener, EventTarget, ListenerPhase};
use crate::dom::eventtarget::{EventListeners, EventTarget, ListenerPhase};
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlinputelement::InputActivationState;
use crate::dom::htmlslotelement::HTMLSlotElement;
@ -507,7 +507,7 @@ impl Event {
// corresponding pre-activation behavior.
pre_activation_result = activation_target
.as_maybe_activatable()
.and_then(|activatable| activatable.legacy_pre_activation_behavior());
.and_then(|activatable| activatable.legacy_pre_activation_behavior(can_gc));
}
let timeline_window = DomRoot::downcast::<Window>(target.global())
@ -584,7 +584,7 @@ impl Event {
if let Some(target) = self.GetTarget() {
if let Some(node) = target.downcast::<Node>() {
let vtable = vtable_for(node);
vtable.handle_event(self);
vtable.handle_event(self, can_gc);
}
}
}
@ -623,7 +623,7 @@ impl Event {
// Step 11.2 Otherwise, if activationTarget has legacy-canceled-activation behavior, then run
// activationTargets legacy-canceled-activation behavior.
else {
activatable.legacy_canceled_activation_behavior(pre_activation_result);
activatable.legacy_canceled_activation_behavior(pre_activation_result, can_gc);
}
}
}
@ -1040,14 +1040,39 @@ impl From<bool> for EventCancelable {
}
impl From<EventCancelable> for bool {
fn from(bubbles: EventCancelable) -> Self {
match bubbles {
fn from(cancelable: EventCancelable) -> Self {
match cancelable {
EventCancelable::Cancelable => true,
EventCancelable::NotCancelable => false,
}
}
}
#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
pub(crate) enum EventComposed {
Composed,
NotComposed,
}
impl From<bool> for EventComposed {
fn from(boolean: bool) -> Self {
if boolean {
EventComposed::Composed
} else {
EventComposed::NotComposed
}
}
}
impl From<EventComposed> for bool {
fn from(composed: EventComposed) -> Self {
match composed {
EventComposed::Composed => true,
EventComposed::NotComposed => false,
}
}
}
#[derive(Clone, Copy, Debug, Eq, JSTraceable, PartialEq)]
#[repr(u16)]
#[derive(MallocSizeOf)]
@ -1151,10 +1176,7 @@ fn invoke(
event.current_target.set(Some(&segment.invocation_target));
// Step 6. Let listeners be a clone of events currentTarget attribute values event listener list.
let listeners =
segment
.invocation_target
.get_listeners_for(&event.type_(), Some(phase), can_gc);
let listeners = segment.invocation_target.get_listeners_for(&event.type_());
// Step 7. Let invocationTargetInShadowTree be structs invocation-target-in-shadow-tree.
let invocation_target_in_shadow_tree = segment.invocation_target_in_shadow_tree;
@ -1207,8 +1229,8 @@ fn invoke(
/// <https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke>
fn inner_invoke(
event: &Event,
listeners: &[CompiledEventListener],
_phase: ListenerPhase,
listeners: &EventListeners,
phase: ListenerPhase,
invocation_target_in_shadow_tree: bool,
timeline_window: Option<&Window>,
can_gc: CanGc,
@ -1217,25 +1239,21 @@ fn inner_invoke(
let mut found = false;
// Step 2. For each listener in listeners, whose removed is false:
for listener in listeners {
// FIXME(#25479): We need an "if !listener.removed()" here,
// but there's a subtlety. Where Servo is currently using the
// CompiledEventListener, we really need something that maps to
// https://dom.spec.whatwg.org/#concept-event-listener
// which is not the same thing as the EventListener interface.
// script::dom::eventtarget::EventListenerEntry is the closest
// match we have, and is already holding the "once" flag,
// but it's not a drop-in replacement.
for listener in listeners.iter() {
if listener.borrow().removed() {
continue;
}
// Steps 2.1 and 2.3-2.4 are not done because `listeners` contain only the
// relevant ones for this invoke call during the dispatch algorithm.
// TODO: Step 2.1 If events type attribute value is not listeners type, then continue.
// Step 2.1 If events type attribute value is not listeners type, then continue.
// Step 2.2. Set found to true.
found = true;
// TODO Step 2.3 If phase is "capturing" and listeners capture is false, then continue.
// TODO Step 2.4 If phase is "bubbling" and listeners capture is true, then continue.
// Step 2.3 If phase is "capturing" and listeners capture is false, then continue.
// Step 2.4 If phase is "bubbling" and listeners capture is true, then continue.
if listener.borrow().phase() != phase {
continue;
}
let event_target = event
.GetCurrentTarget()
@ -1243,12 +1261,20 @@ fn inner_invoke(
// Step 2.5 If listeners once is true, then remove an event listener given events currentTarget
// attribute value and listener.
if let CompiledEventListener::Listener(event_listener) = listener {
event_target.remove_listener_if_once(&event.type_(), event_listener);
if listener.borrow().once() {
event_target.remove_listener(&event.type_(), listener);
}
let Some(compiled_listener) =
listener
.borrow()
.get_compiled_listener(&event_target, &event.type_(), can_gc)
else {
continue;
};
// Step 2.6 Let global be listener callbacks associated realms global object.
let global = listener.associated_global();
let global = compiled_listener.associated_global();
// Step 2.7 Let currentEvent be undefined.
let mut current_event = None;
@ -1264,18 +1290,22 @@ fn inner_invoke(
}
// Step 2.9 If listeners passive is true, then set event's in passive listener flag.
if let CompiledEventListener::Listener(event_listener) = listener {
event.set_in_passive_listener(event_target.is_passive(&event.type_(), event_listener));
}
event.set_in_passive_listener(event_target.is_passive(&event.type_(), listener));
// Step 2.10 If global is a Window object, then record timing info for event listener given event and listener.
// Step 2.10 If global is a Window object, then record timing info for event listener
// given event and listener.
// Step 2.11 Call a user objects operation with listeners callback, "handleEvent", « event »,
// and events currentTarget attribute value. If this throws an exception exception:
// Step 2.10.1 Report exception for listeners callbacks corresponding JavaScript objects
// associated realms global object.
// TODO Step 2.10.2 Set legacyOutputDidListenersThrowFlag if given.
let marker = TimelineMarker::start("DOMEvent".to_owned());
listener.call_or_handle_event(&event_target, event, ExceptionHandling::Report, can_gc);
compiled_listener.call_or_handle_event(
&event_target,
event,
ExceptionHandling::Report,
can_gc,
);
if let Some(window) = timeline_window {
window.emit_timeline_marker(marker.end());
}

View file

@ -8,8 +8,10 @@ use std::str::{Chars, FromStr};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use content_security_policy as csp;
use dom_struct::dom_struct;
use headers::ContentType;
use http::StatusCode;
use http::header::{self, HeaderName, HeaderValue};
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
@ -182,7 +184,9 @@ impl EventSourceContext {
self.data.push_str(&self.value);
self.data.push('\n');
},
"id" => mem::swap(&mut self.last_event_id, &mut self.value),
"id" if !self.value.contains('\0') => {
mem::swap(&mut self.last_event_id, &mut self.value);
},
"retry" => {
if let Ok(time) = u64::from_str(&self.value) {
self.event_source
@ -348,6 +352,11 @@ impl FetchResponseListener for EventSourceContext {
_ => unsafe_,
},
};
// Step 15.3 if res's status is not 200, or if res's `Content-Type` is not
// `text/event-stream`, then fail the connection.
if meta.status.code() != StatusCode::OK {
return self.fail_the_connection();
}
let mime = match meta.content_type {
None => return self.fail_the_connection(),
Some(ct) => <ContentType as Into<Mime>>::into(ct.into_inner()),
@ -356,12 +365,15 @@ impl FetchResponseListener for EventSourceContext {
return self.fail_the_connection();
}
self.origin = meta.final_url.origin().ascii_serialization();
// Step 15.4 announce the connection and interpret res's body line by line.
self.announce_the_connection();
},
Err(_) => {
// The spec advises failing here if reconnecting would be
// "futile", with no more specific advice; WPT tests
// consider a non-http(s) scheme to be futile.
// Step 15.2 if res is a network error, then reestablish the connection, unless
// the user agent knows that to be futile, in which case the user agent may
// fail the connection.
// WPT tests consider a non-http(s) scheme to be futile.
match self.event_source.root().url.scheme() {
"http" | "https" => self.reestablish_the_connection(),
_ => self.fail_the_connection(),
@ -412,12 +424,14 @@ impl FetchResponseListener for EventSourceContext {
fn process_response_eof(
&mut self,
_: RequestId,
_response: Result<ResourceFetchTiming, NetworkError>,
response: Result<ResourceFetchTiming, NetworkError>,
) {
if self.incomplete_utf8.take().is_some() {
self.parse("\u{FFFD}".chars(), CanGc::note());
}
self.reestablish_the_connection();
if response.is_ok() {
self.reestablish_the_connection();
}
}
fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
@ -431,6 +445,11 @@ impl FetchResponseListener for EventSourceContext {
fn submit_resource_timing(&mut self) {
network_listener::submit_timing(self, CanGc::note())
}
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<csp::Violation>) {
let global = &self.resource_timing_global();
global.report_csp_violations(violations, None);
}
}
impl ResourceTimingListener for EventSourceContext {
@ -529,29 +548,35 @@ impl EventSourceMethods<crate::DomTypeHolder> for EventSource {
event_source_init: &EventSourceInit,
) -> Fallible<DomRoot<EventSource>> {
// TODO: Step 2 relevant settings object
// Step 3
// Step 3 Let urlRecord be the result of encoding-parsing a URL given url,
// relative to settings.
let base_url = global.api_base_url();
let url_record = match base_url.join(&url) {
Ok(u) => u,
// Step 4
// Step 4 If urlRecord is failure, then throw a "SyntaxError" DOMException.
Err(_) => return Err(Error::Syntax),
};
// Step 1, 5
// Step 1 Let ev be a new EventSource object.
let ev = EventSource::new(
global,
proto,
// Step 5 Set ev's url to urlRecord.
url_record.clone(),
event_source_init.withCredentials,
can_gc,
);
global.track_event_source(&ev);
// Steps 6-7
let cors_attribute_state = if event_source_init.withCredentials {
// Step 7 If the value of eventSourceInitDict's withCredentials member is true,
// then set corsAttributeState to Use Credentials and set ev's withCredentials
// attribute to true.
CorsSettings::UseCredentials
} else {
// Step 6 Let corsAttributeState be Anonymous.
CorsSettings::Anonymous
};
// Step 8
// Step 8 Let request be the result of creating a potential-CORS request
// given urlRecord, the empty string, and corsAttributeState.
// TODO: Step 9 set request's client settings
let mut request = create_a_potential_cors_request(
global.webview_id(),
@ -561,21 +586,24 @@ impl EventSourceMethods<crate::DomTypeHolder> for EventSource {
Some(true),
global.get_referrer(),
global.insecure_requests_policy(),
global.has_trustworthy_ancestor_or_current_origin(),
global.policy_container(),
)
.origin(global.origin().immutable().clone())
.pipeline_id(Some(global.pipeline_id()));
// Step 10
// Step 10 User agents may set (`Accept`, `text/event-stream`) in request's header list.
// TODO(eijebong): Replace once typed headers allow it
request.headers.insert(
header::ACCEPT,
HeaderValue::from_static("text/event-stream"),
);
// Step 11
// Step 11 Set request's cache mode to "no-store".
request.cache_mode = CacheMode::NoStore;
// Step 12
// Step 13 Set ev's request to request.
*ev.request.borrow_mut() = Some(request.clone());
// Step 14
// Step 14 Let processEventSourceEndOfBody given response res be the following step:
// if res is not a network error, then reestablish the connection.
let (action_sender, action_receiver) = ipc::channel().unwrap();
let context = EventSourceContext {
incomplete_utf8: None,
@ -612,7 +640,7 @@ impl EventSourceMethods<crate::DomTypeHolder> for EventSource {
FetchChannels::ResponseMsg(action_sender),
))
.unwrap();
// Step 13
// Step 16 Return ev.
Ok(ev)
}

View file

@ -11,6 +11,7 @@ use std::mem;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use content_security_policy as csp;
use deny_public_fields::DenyPublicFields;
use dom_struct::dom_struct;
use fnv::FnvHasher;
@ -307,11 +308,36 @@ impl CompiledEventListener {
// (as distinct from https://dom.spec.whatwg.org/#callbackdef-eventlistener)
#[derive(Clone, DenyPublicFields, JSTraceable, MallocSizeOf)]
/// A listener in a collection of event listeners.
struct EventListenerEntry {
pub(crate) struct EventListenerEntry {
phase: ListenerPhase,
listener: EventListenerType,
once: bool,
passive: Option<bool>,
removed: bool,
}
impl EventListenerEntry {
pub(crate) fn phase(&self) -> ListenerPhase {
self.phase
}
pub(crate) fn once(&self) -> bool {
self.once
}
pub(crate) fn removed(&self) -> bool {
self.removed
}
/// <https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler>
pub(crate) fn get_compiled_listener(
&self,
owner: &EventTarget,
ty: &Atom,
can_gc: CanGc,
) -> Option<CompiledEventListener> {
self.listener.get_compiled_listener(owner, ty, can_gc)
}
}
impl std::cmp::PartialEq for EventListenerEntry {
@ -320,19 +346,21 @@ impl std::cmp::PartialEq for EventListenerEntry {
}
}
#[derive(JSTraceable, MallocSizeOf)]
#[derive(Clone, JSTraceable, MallocSizeOf)]
/// A mix of potentially uncompiled and compiled event listeners of the same type.
struct EventListeners(Vec<EventListenerEntry>);
pub(crate) struct EventListeners(
#[ignore_malloc_size_of = "Rc"] Vec<Rc<RefCell<EventListenerEntry>>>,
);
impl Deref for EventListeners {
type Target = Vec<EventListenerEntry>;
fn deref(&self) -> &Vec<EventListenerEntry> {
type Target = Vec<Rc<RefCell<EventListenerEntry>>>;
fn deref(&self) -> &Vec<Rc<RefCell<EventListenerEntry>>> {
&self.0
}
}
impl DerefMut for EventListeners {
fn deref_mut(&mut self) -> &mut Vec<EventListenerEntry> {
fn deref_mut(&mut self) -> &mut Vec<Rc<RefCell<EventListenerEntry>>> {
&mut self.0
}
}
@ -346,7 +374,7 @@ impl EventListeners {
can_gc: CanGc,
) -> Option<CommonEventHandler> {
for entry in &self.0 {
if let EventListenerType::Inline(ref inline) = entry.listener {
if let EventListenerType::Inline(ref inline) = entry.borrow().listener {
// Step 1.1-1.8 and Step 2
return get_compiled_handler(inline, owner, ty, can_gc);
}
@ -356,30 +384,7 @@ impl EventListeners {
None
}
// https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler
fn get_listeners(
&self,
phase: Option<ListenerPhase>,
owner: &EventTarget,
ty: &Atom,
can_gc: CanGc,
) -> Vec<CompiledEventListener> {
self.0
.iter()
.filter_map(|entry| {
if phase.is_none() || Some(entry.phase) == phase {
// Step 1.1-1.8, 2
entry.listener.get_compiled_listener(owner, ty, can_gc)
} else {
None
}
})
.collect()
}
fn has_listeners(&self) -> bool {
// TODO: add, and take into account, a 'removed' field?
// https://dom.spec.whatwg.org/#event-listener-removed
!self.0.is_empty()
}
}
@ -420,18 +425,11 @@ impl EventTarget {
}
}
pub(crate) fn get_listeners_for(
&self,
type_: &Atom,
specific_phase: Option<ListenerPhase>,
can_gc: CanGc,
) -> Vec<CompiledEventListener> {
pub(crate) fn get_listeners_for(&self, type_: &Atom) -> EventListeners {
self.handlers
.borrow()
.get(type_)
.map_or(vec![], |listeners| {
listeners.get_listeners(specific_phase, self, type_, can_gc)
})
.map_or(EventListeners(vec![]), |listeners| listeners.clone())
}
pub(crate) fn dispatch_event(&self, event: &Event, can_gc: CanGc) -> EventStatus {
@ -439,7 +437,14 @@ impl EventTarget {
}
pub(crate) fn remove_all_listeners(&self) {
*self.handlers.borrow_mut() = Default::default();
let mut handlers = self.handlers.borrow_mut();
for (_, entries) in handlers.iter() {
entries
.iter()
.for_each(|entry| entry.borrow_mut().removed = true);
}
*handlers = Default::default();
}
/// <https://dom.spec.whatwg.org/#default-passive-value>
@ -488,50 +493,49 @@ impl EventTarget {
let idx = entries
.iter()
.position(|entry| matches!(entry.listener, EventListenerType::Inline(_)));
.position(|entry| matches!(entry.borrow().listener, EventListenerType::Inline(_)));
match idx {
Some(idx) => match listener {
// Replace if there's something to replace with,
// but remove entirely if there isn't.
Some(listener) => {
entries[idx].listener = EventListenerType::Inline(listener.into());
entries[idx].borrow_mut().listener = EventListenerType::Inline(listener.into());
},
None => {
entries.remove(idx);
entries.remove(idx).borrow_mut().removed = true;
},
},
None => {
if let Some(listener) = listener {
entries.push(EventListenerEntry {
entries.push(Rc::new(RefCell::new(EventListenerEntry {
phase: ListenerPhase::Bubbling,
listener: EventListenerType::Inline(listener.into()),
once: false,
passive: None,
});
removed: false,
})));
}
},
}
}
pub(crate) fn remove_listener_if_once(&self, ty: &Atom, listener: &Rc<EventListener>) {
pub(crate) fn remove_listener(&self, ty: &Atom, entry: &Rc<RefCell<EventListenerEntry>>) {
let mut handlers = self.handlers.borrow_mut();
let listener = EventListenerType::Additive(listener.clone());
if let Some(entries) = handlers.get_mut(ty) {
entries.retain(|e| e.listener != listener || !e.once)
if let Some(position) = entries.iter().position(|e| *e == *entry) {
entries.remove(position).borrow_mut().removed = true;
}
}
}
/// Determines the `passive` attribute of an associated event listener
pub(crate) fn is_passive(&self, ty: &Atom, listener: &Rc<EventListener>) -> bool {
let handlers = self.handlers.borrow();
let listener_instance = EventListenerType::Additive(listener.clone());
handlers
.get(ty)
.and_then(|entries| entries.iter().find(|e| e.listener == listener_instance))
.is_some_and(|entry| entry.passive.unwrap_or(self.default_passive_value(ty)))
pub(crate) fn is_passive(&self, ty: &Atom, listener: &Rc<RefCell<EventListenerEntry>>) -> bool {
listener
.borrow()
.passive
.unwrap_or(self.default_passive_value(ty))
}
fn get_inline_event_listener(&self, ty: &Atom, can_gc: CanGc) -> Option<CommonEventHandler> {
@ -548,9 +552,25 @@ impl EventTarget {
url: ServoUrl,
line: usize,
ty: &str,
source: DOMString,
source: &str,
) {
let handler = InternalRawUncompiledHandler { source, line, url };
if let Some(element) = self.downcast::<Element>() {
let doc = element.owner_document();
if doc.should_elements_inline_type_behavior_be_blocked(
element.upcast(),
csp::InlineCheckType::ScriptAttribute,
source,
) == csp::CheckResult::Blocked
{
return;
}
};
let handler = InternalRawUncompiledHandler {
source: DOMString::from(source),
line,
url,
};
self.set_inline_event_listener(
Atom::from(ty),
Some(InlineEventListener::Uncompiled(handler)),
@ -810,7 +830,7 @@ impl EventTarget {
None => return,
};
let mut handlers = self.handlers.borrow_mut();
let entry = match handlers.entry(Atom::from(ty)) {
let entries = match handlers.entry(Atom::from(ty)) {
Occupied(entry) => entry.into_mut(),
Vacant(entry) => entry.insert(EventListeners(vec![])),
};
@ -820,14 +840,16 @@ impl EventTarget {
} else {
ListenerPhase::Bubbling
};
let new_entry = EventListenerEntry {
let new_entry = Rc::new(RefCell::new(EventListenerEntry {
phase,
listener: EventListenerType::Additive(listener),
once: options.once,
passive: options.passive,
};
if !entry.contains(&new_entry) {
entry.push(new_entry);
removed: false,
}));
if !entries.contains(&new_entry) {
entries.push(new_entry);
}
}
@ -842,21 +864,21 @@ impl EventTarget {
return;
};
let mut handlers = self.handlers.borrow_mut();
let entry = handlers.get_mut(&Atom::from(ty));
if let Some(entry) = entry {
if let Some(entries) = handlers.get_mut(&Atom::from(ty)) {
let phase = if options.capture {
ListenerPhase::Capturing
} else {
ListenerPhase::Bubbling
};
let old_entry = EventListenerEntry {
let old_entry = Rc::new(RefCell::new(EventListenerEntry {
phase,
listener: EventListenerType::Additive(listener.clone()),
once: false,
passive: None,
};
if let Some(position) = entry.iter().position(|e| *e == old_entry) {
entry.remove(position);
removed: false,
}));
if let Some(position) = entries.iter().position(|e| *e == old_entry) {
entries.remove(position).borrow_mut().removed = true;
}
}
}

View file

@ -4,10 +4,10 @@
use std::time::SystemTime;
use constellation_traits::BlobImpl;
use dom_struct::dom_struct;
use js::rust::HandleObject;
use net_traits::filemanager_thread::SelectedFile;
use script_traits::serializable::BlobImpl;
use time::{Duration, OffsetDateTime};
use crate::dom::bindings::codegen::Bindings::FileBinding;
@ -108,6 +108,10 @@ impl File {
pub(crate) fn file_type(&self) -> String {
self.blob.type_string()
}
pub(crate) fn get_modified(&self) -> SystemTime {
self.modified
}
}
impl FileMethods<crate::DomTypeHolder> for File {
@ -132,15 +136,12 @@ impl FileMethods<crate::DomTypeHolder> for File {
.map(|modified| OffsetDateTime::UNIX_EPOCH + Duration::milliseconds(modified))
.map(Into::into);
// NOTE: Following behaviour might be removed in future,
// see https://github.com/w3c/FileAPI/issues/41
let replaced_filename = DOMString::from_string(filename.replace('/', ":"));
let type_string = normalize_type_string(blobPropertyBag.type_.as_ref());
Ok(File::new_with_proto(
global,
proto,
BlobImpl::new_from_bytes(bytes, type_string),
replaced_filename,
filename,
modified,
can_gc,
))

View file

@ -14,6 +14,7 @@ use js::jsval::{self, JSVal};
use js::rust::HandleObject;
use js::typedarray::{ArrayBuffer, CreateWith};
use mime::{self, Mime};
use script_bindings::num::Finite;
use stylo_atoms::Atom;
use crate::dom::bindings::cell::DomRefCell;
@ -463,8 +464,8 @@ impl FileReader {
EventBubbles::DoesNotBubble,
EventCancelable::NotCancelable,
total.is_some(),
loaded,
total.unwrap_or(0),
Finite::wrap(loaded as f64),
Finite::wrap(total.unwrap_or(0) as f64),
can_gc,
);
progressevent.upcast::<Event>().fire(self.upcast(), can_gc);

View file

@ -15,7 +15,7 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::event::{EventBubbles, EventCancelable};
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::eventtarget::EventTarget;
use crate::dom::uievent::UIEvent;
use crate::dom::window::Window;
@ -118,6 +118,9 @@ impl FocusEventMethods<crate::DomTypeHolder> for FocusEvent {
init.relatedTarget.as_deref(),
can_gc,
);
event
.upcast::<Event>()
.set_composed(init.parent.parent.composed);
Ok(event)
}

View file

@ -2,10 +2,10 @@
* 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 constellation_traits::BlobImpl;
use dom_struct::dom_struct;
use html5ever::LocalName;
use js::rust::HandleObject;
use script_traits::serializable::BlobImpl;
use super::bindings::trace::NoTrace;
use crate::dom::bindings::cell::DomRefCell;
@ -275,12 +275,13 @@ impl FormData {
};
let bytes = blob.get_bytes().unwrap_or_default();
let last_modified = blob.downcast::<File>().map(|file| file.get_modified());
File::new(
&self.global(),
BlobImpl::new_from_bytes(bytes, blob.type_string()),
name,
None,
last_modified,
can_gc,
)
}

View file

@ -11,12 +11,11 @@ use crate::dom::bindings::codegen::Bindings::FormDataEventBinding;
use crate::dom::bindings::codegen::Bindings::FormDataEventBinding::FormDataEventMethods;
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto};
use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::formdata::FormData;
use crate::dom::globalscope::GlobalScope;
use crate::dom::window::Window;
use crate::script_runtime::CanGc;
@ -28,7 +27,7 @@ pub(crate) struct FormDataEvent {
impl FormDataEvent {
pub(crate) fn new(
global: &GlobalScope,
window: &Window,
type_: Atom,
can_bubble: EventBubbles,
cancelable: EventCancelable,
@ -36,12 +35,12 @@ impl FormDataEvent {
can_gc: CanGc,
) -> DomRoot<FormDataEvent> {
Self::new_with_proto(
global, None, type_, can_bubble, cancelable, form_data, can_gc,
window, None, type_, can_bubble, cancelable, form_data, can_gc,
)
}
fn new_with_proto(
global: &GlobalScope,
window: &Window,
proto: Option<HandleObject>,
type_: Atom,
can_bubble: EventBubbles,
@ -54,7 +53,7 @@ impl FormDataEvent {
event: Event::new_inherited(),
form_data: Dom::from_ref(form_data),
}),
global,
window,
proto,
can_gc,
);
@ -80,7 +79,7 @@ impl FormDataEventMethods<crate::DomTypeHolder> for FormDataEvent {
let cancelable = EventCancelable::from(init.parent.cancelable);
let event = FormDataEvent::new_with_proto(
&window.global(),
window,
proto,
Atom::from(type_),
bubbles,

View file

@ -13,7 +13,7 @@ use crate::dom::bindings::codegen::Bindings::GamepadBinding::{GamepadHand, Gamep
use crate::dom::bindings::codegen::Bindings::GamepadButtonListBinding::GamepadButtonListMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::event::Event;
@ -23,6 +23,7 @@ use crate::dom::gamepadevent::{GamepadEvent, GamepadEventType};
use crate::dom::gamepadhapticactuator::GamepadHapticActuator;
use crate::dom::gamepadpose::GamepadPose;
use crate::dom::globalscope::GlobalScope;
use crate::dom::window::Window;
use crate::script_runtime::{CanGc, JSContext};
// This value is for determining when to consider a gamepad as having a user gesture
@ -88,39 +89,14 @@ impl Gamepad {
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
global: &GlobalScope,
gamepad_id: u32,
id: String,
mapping_type: String,
axis_bounds: (f64, f64),
button_bounds: (f64, f64),
supported_haptic_effects: GamepadSupportedHapticEffects,
xr: bool,
can_gc: CanGc,
) -> DomRoot<Gamepad> {
Self::new_with_proto(
global,
gamepad_id,
id,
mapping_type,
axis_bounds,
button_bounds,
supported_haptic_effects,
xr,
can_gc,
)
}
/// When we construct a new gamepad, we initialize the number of buttons and
/// axes corresponding to the "standard" gamepad mapping.
/// The spec says UAs *may* do this for fingerprint mitigation, and it also
/// happens to simplify implementation
/// <https://www.w3.org/TR/gamepad/#fingerprinting-mitigation>
#[allow(clippy::too_many_arguments)]
fn new_with_proto(
global: &GlobalScope,
pub(crate) fn new(
window: &Window,
gamepad_id: u32,
id: String,
mapping_type: String,
@ -130,11 +106,11 @@ impl Gamepad {
xr: bool,
can_gc: CanGc,
) -> DomRoot<Gamepad> {
let button_list = GamepadButtonList::init_buttons(global, can_gc);
let button_list = GamepadButtonList::init_buttons(window, can_gc);
let vibration_actuator =
GamepadHapticActuator::new(global, gamepad_id, supported_haptic_effects, can_gc);
GamepadHapticActuator::new(window, gamepad_id, supported_haptic_effects, can_gc);
let index = if xr { -1 } else { 0 };
let gamepad = reflect_dom_object_with_proto(
let gamepad = reflect_dom_object(
Box::new(Gamepad::new_inherited(
gamepad_id,
id,
@ -149,8 +125,7 @@ impl Gamepad {
button_bounds,
&vibration_actuator,
)),
global,
None,
window,
can_gc,
);
gamepad.init_axes(can_gc);
@ -248,7 +223,8 @@ impl Gamepad {
}
pub(crate) fn notify_event(&self, event_type: GamepadEventType, can_gc: CanGc) {
let event = GamepadEvent::new_with_type(&self.global(), event_type, self, can_gc);
let event =
GamepadEvent::new_with_type(self.global().as_window(), event_type, self, can_gc);
event
.upcast::<Event>()
.fire(self.global().as_window().upcast::<EventTarget>(), can_gc);

View file

@ -10,7 +10,7 @@ use crate::dom::bindings::codegen::Bindings::GamepadButtonBinding::GamepadButton
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::globalscope::GlobalScope;
use crate::dom::window::Window;
use crate::script_runtime::CanGc;
#[dom_struct]
@ -32,14 +32,14 @@ impl GamepadButton {
}
pub(crate) fn new(
global: &GlobalScope,
window: &Window,
pressed: bool,
touched: bool,
can_gc: CanGc,
) -> DomRoot<GamepadButton> {
reflect_dom_object(
Box::new(GamepadButton::new_inherited(pressed, touched)),
global,
window,
can_gc,
)
}

View file

@ -8,7 +8,7 @@ use crate::dom::bindings::codegen::Bindings::GamepadButtonListBinding::GamepadBu
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot, DomSlice};
use crate::dom::gamepadbutton::GamepadButton;
use crate::dom::globalscope::GlobalScope;
use crate::dom::window::Window;
use crate::script_runtime::CanGc;
// https://w3c.github.io/gamepad/#gamepadbutton-interface
@ -28,13 +28,13 @@ impl GamepadButtonList {
}
pub(crate) fn new(
global: &GlobalScope,
window: &Window,
list: &[&GamepadButton],
can_gc: CanGc,
) -> DomRoot<GamepadButtonList> {
reflect_dom_object(
Box::new(GamepadButtonList::new_inherited(list)),
global,
window,
can_gc,
)
}
@ -62,27 +62,27 @@ impl GamepadButtonListMethods<crate::DomTypeHolder> for GamepadButtonList {
impl GamepadButtonList {
/// Initialize the number of buttons in the "standard" gamepad mapping.
/// <https://www.w3.org/TR/gamepad/#dfn-initializing-buttons>
pub(crate) fn init_buttons(global: &GlobalScope, can_gc: CanGc) -> DomRoot<GamepadButtonList> {
pub(crate) fn init_buttons(window: &Window, can_gc: CanGc) -> DomRoot<GamepadButtonList> {
let standard_buttons = &[
GamepadButton::new(global, false, false, can_gc), // Bottom button in right cluster
GamepadButton::new(global, false, false, can_gc), // Right button in right cluster
GamepadButton::new(global, false, false, can_gc), // Left button in right cluster
GamepadButton::new(global, false, false, can_gc), // Top button in right cluster
GamepadButton::new(global, false, false, can_gc), // Top left front button
GamepadButton::new(global, false, false, can_gc), // Top right front button
GamepadButton::new(global, false, false, can_gc), // Bottom left front button
GamepadButton::new(global, false, false, can_gc), // Bottom right front button
GamepadButton::new(global, false, false, can_gc), // Left button in center cluster
GamepadButton::new(global, false, false, can_gc), // Right button in center cluster
GamepadButton::new(global, false, false, can_gc), // Left stick pressed button
GamepadButton::new(global, false, false, can_gc), // Right stick pressed button
GamepadButton::new(global, false, false, can_gc), // Top button in left cluster
GamepadButton::new(global, false, false, can_gc), // Bottom button in left cluster
GamepadButton::new(global, false, false, can_gc), // Left button in left cluster
GamepadButton::new(global, false, false, can_gc), // Right button in left cluster
GamepadButton::new(global, false, false, can_gc), // Center button in center cluster
GamepadButton::new(window, false, false, can_gc), // Bottom button in right cluster
GamepadButton::new(window, false, false, can_gc), // Right button in right cluster
GamepadButton::new(window, false, false, can_gc), // Left button in right cluster
GamepadButton::new(window, false, false, can_gc), // Top button in right cluster
GamepadButton::new(window, false, false, can_gc), // Top left front button
GamepadButton::new(window, false, false, can_gc), // Top right front button
GamepadButton::new(window, false, false, can_gc), // Bottom left front button
GamepadButton::new(window, false, false, can_gc), // Bottom right front button
GamepadButton::new(window, false, false, can_gc), // Left button in center cluster
GamepadButton::new(window, false, false, can_gc), // Right button in center cluster
GamepadButton::new(window, false, false, can_gc), // Left stick pressed button
GamepadButton::new(window, false, false, can_gc), // Right stick pressed button
GamepadButton::new(window, false, false, can_gc), // Top button in left cluster
GamepadButton::new(window, false, false, can_gc), // Bottom button in left cluster
GamepadButton::new(window, false, false, can_gc), // Left button in left cluster
GamepadButton::new(window, false, false, can_gc), // Right button in left cluster
GamepadButton::new(window, false, false, can_gc), // Center button in center cluster
];
rooted_vec!(let buttons <- standard_buttons.iter().map(DomRoot::as_traced));
Self::new(global, buttons.r(), can_gc)
Self::new(window, buttons.r(), can_gc)
}
}

View file

@ -11,12 +11,11 @@ use crate::dom::bindings::codegen::Bindings::GamepadEventBinding;
use crate::dom::bindings::codegen::Bindings::GamepadEventBinding::GamepadEventMethods;
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto};
use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::event::Event;
use crate::dom::gamepad::Gamepad;
use crate::dom::globalscope::GlobalScope;
use crate::dom::window::Window;
use crate::script_runtime::CanGc;
@ -40,18 +39,18 @@ impl GamepadEvent {
}
pub(crate) fn new(
global: &GlobalScope,
window: &Window,
type_: Atom,
bubbles: bool,
cancelable: bool,
gamepad: &Gamepad,
can_gc: CanGc,
) -> DomRoot<GamepadEvent> {
Self::new_with_proto(global, None, type_, bubbles, cancelable, gamepad, can_gc)
Self::new_with_proto(window, None, type_, bubbles, cancelable, gamepad, can_gc)
}
fn new_with_proto(
global: &GlobalScope,
window: &Window,
proto: Option<HandleObject>,
type_: Atom,
bubbles: bool,
@ -61,7 +60,7 @@ impl GamepadEvent {
) -> DomRoot<GamepadEvent> {
let ev = reflect_dom_object_with_proto(
Box::new(GamepadEvent::new_inherited(gamepad)),
global,
window,
proto,
can_gc,
);
@ -73,7 +72,7 @@ impl GamepadEvent {
}
pub(crate) fn new_with_type(
global: &GlobalScope,
window: &Window,
event_type: GamepadEventType,
gamepad: &Gamepad,
can_gc: CanGc,
@ -83,7 +82,7 @@ impl GamepadEvent {
GamepadEventType::Disconnected => "gamepaddisconnected",
};
GamepadEvent::new(global, name.into(), false, false, gamepad, can_gc)
GamepadEvent::new(window, name.into(), false, false, gamepad, can_gc)
}
}
@ -97,7 +96,7 @@ impl GamepadEventMethods<crate::DomTypeHolder> for GamepadEvent {
init: &GamepadEventBinding::GamepadEventInit,
) -> Fallible<DomRoot<GamepadEvent>> {
Ok(GamepadEvent::new_with_proto(
&window.global(),
window,
proto,
Atom::from(type_),
init.parent.bubbles,

View file

@ -18,12 +18,12 @@ use crate::dom::bindings::codegen::Bindings::GamepadHapticActuatorBinding::{
use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::utils::to_frozen_array;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::window::Window;
use crate::realms::InRealm;
use crate::script_runtime::{CanGc, JSContext};
use crate::task_source::SendableTaskSource;
@ -98,27 +98,17 @@ impl GamepadHapticActuator {
}
pub(crate) fn new(
global: &GlobalScope,
window: &Window,
gamepad_index: u32,
supported_haptic_effects: GamepadSupportedHapticEffects,
can_gc: CanGc,
) -> DomRoot<GamepadHapticActuator> {
Self::new_with_proto(global, gamepad_index, supported_haptic_effects, can_gc)
}
fn new_with_proto(
global: &GlobalScope,
gamepad_index: u32,
supported_haptic_effects: GamepadSupportedHapticEffects,
can_gc: CanGc,
) -> DomRoot<GamepadHapticActuator> {
reflect_dom_object_with_proto(
reflect_dom_object(
Box::new(GamepadHapticActuator::new_inherited(
gamepad_index,
supported_haptic_effects,
)),
global,
None,
window,
can_gc,
)
}

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use script_layout_interface::HTMLCanvasDataSource;
use webrender_api::ImageKey;
use crate::dom::bindings::codegen::Bindings::GPUCanvasContextBinding::GPUCanvasContextMethods;
use crate::dom::bindings::codegen::UnionTypes;
@ -31,7 +31,7 @@ impl GPUCanvasContextMethods<crate::DomTypeHolder> for GPUCanvasContext {
}
impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, GPUCanvasContext> {
fn canvas_data_source(self) -> HTMLCanvasDataSource {
fn canvas_data_source(self) -> Option<ImageKey> {
unimplemented!()
}
}

View file

@ -6,7 +6,9 @@ use std::cell::Cell;
use std::cmp::Ordering;
use base::id::HistoryStateId;
use constellation_traits::TraversalDirection;
use constellation_traits::{
ScriptToConstellationMessage, StructuredSerializedData, TraversalDirection,
};
use dom_struct::dom_struct;
use js::jsapi::Heap;
use js::jsval::{JSVal, NullValue, UndefinedValue};
@ -14,7 +16,6 @@ use js::rust::{HandleValue, MutableHandleValue};
use net_traits::{CoreResourceMsg, IpcSend};
use profile_traits::ipc;
use profile_traits::ipc::channel;
use script_traits::{ScriptMsg, StructuredSerializedData};
use servo_url::ServoUrl;
use crate::dom::bindings::codegen::Bindings::HistoryBinding::HistoryMethods;
@ -72,7 +73,7 @@ impl History {
if !self.window.Document().is_fully_active() {
return Err(Error::Security);
}
let msg = ScriptMsg::TraverseHistory(direction);
let msg = ScriptToConstellationMessage::TraverseHistory(direction);
let _ = self
.window
.as_global_scope()
@ -227,7 +228,7 @@ impl History {
PushOrReplace::Push => {
let state_id = HistoryStateId::new();
self.state_id.set(Some(state_id));
let msg = ScriptMsg::PushHistoryState(state_id, new_url.clone());
let msg = ScriptToConstellationMessage::PushHistoryState(state_id, new_url.clone());
let _ = self
.window
.as_global_scope()
@ -244,7 +245,8 @@ impl History {
state_id
},
};
let msg = ScriptMsg::ReplaceHistoryState(state_id, new_url.clone());
let msg =
ScriptToConstellationMessage::ReplaceHistoryState(state_id, new_url.clone());
let _ = self
.window
.as_global_scope()
@ -339,7 +341,7 @@ impl HistoryMethods<crate::DomTypeHolder> for History {
}
let (sender, recv) = channel(self.global().time_profiler_chan().clone())
.expect("Failed to create channel to send jsh length.");
let msg = ScriptMsg::JointSessionHistoryLength(sender);
let msg = ScriptToConstellationMessage::JointSessionHistoryLength(sender);
let _ = self
.window
.as_global_scope()

View file

@ -100,8 +100,10 @@ impl VirtualMethods for HTMLAnchorElement {
}
}
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
self.super_type().unwrap().attribute_mutated(attr, mutation);
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
self.super_type()
.unwrap()
.attribute_mutated(attr, mutation, can_gc);
match *attr.local_name() {
local_name!("rel") | local_name!("rev") => {
@ -112,9 +114,9 @@ impl VirtualMethods for HTMLAnchorElement {
}
}
fn bind_to_tree(&self, context: &BindContext) {
fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
if let Some(s) = self.super_type() {
s.bind_to_tree(context);
s.bind_to_tree(context, can_gc);
}
self.relations
@ -143,7 +145,7 @@ impl HTMLAnchorElementMethods<crate::DomTypeHolder> for HTMLAnchorElement {
}
// https://html.spec.whatwg.org/multipage/#dom-a-rellist
fn RelList(&self) -> DomRoot<DOMTokenList> {
fn RelList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
self.rel_list.or_init(|| {
DOMTokenList::new(
self.upcast(),
@ -153,7 +155,7 @@ impl HTMLAnchorElementMethods<crate::DomTypeHolder> for HTMLAnchorElement {
Atom::from("noreferrer"),
Atom::from("opener"),
]),
CanGc::note(),
can_gc,
)
})
}

View file

@ -321,8 +321,10 @@ impl VirtualMethods for HTMLAreaElement {
}
}
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
self.super_type().unwrap().attribute_mutated(attr, mutation);
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
self.super_type()
.unwrap()
.attribute_mutated(attr, mutation, can_gc);
match *attr.local_name() {
local_name!("rel") | local_name!("rev") => {
@ -333,9 +335,9 @@ impl VirtualMethods for HTMLAreaElement {
}
}
fn bind_to_tree(&self, context: &BindContext) {
fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
if let Some(s) = self.super_type() {
s.bind_to_tree(context);
s.bind_to_tree(context, can_gc);
}
self.relations
@ -360,7 +362,7 @@ impl HTMLAreaElementMethods<crate::DomTypeHolder> for HTMLAreaElement {
}
/// <https://html.spec.whatwg.org/multipage/#dom-area-rellist>
fn RelList(&self) -> DomRoot<DOMTokenList> {
fn RelList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
self.rel_list.or_init(|| {
DOMTokenList::new(
self.upcast(),
@ -370,7 +372,7 @@ impl HTMLAreaElementMethods<crate::DomTypeHolder> for HTMLAreaElement {
Atom::from("noreferrer"),
Atom::from("opener"),
]),
CanGc::note(),
can_gc,
)
})
}

View file

@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix, QualName, local_name, namespace_url, ns};
use html5ever::{LocalName, Prefix, QualName, local_name, ns};
use js::rust::HandleObject;
use crate::dom::bindings::codegen::Bindings::ElementBinding::Element_Binding::ElementMethods;

View file

@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix, local_name, namespace_url, ns};
use html5ever::{LocalName, Prefix, local_name, ns};
use js::rust::HandleObject;
use servo_url::ServoUrl;
@ -117,20 +117,22 @@ impl VirtualMethods for HTMLBaseElement {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
self.super_type().unwrap().attribute_mutated(attr, mutation);
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
self.super_type()
.unwrap()
.attribute_mutated(attr, mutation, can_gc);
if *attr.local_name() == local_name!("href") {
self.owner_document().refresh_base_element();
}
}
fn bind_to_tree(&self, context: &BindContext) {
self.super_type().unwrap().bind_to_tree(context);
fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
self.super_type().unwrap().bind_to_tree(context, can_gc);
self.bind_unbind(context.tree_is_in_a_document_tree);
}
fn unbind_from_tree(&self, context: &UnbindContext) {
self.super_type().unwrap().unbind_from_tree(context);
fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
self.super_type().unwrap().unbind_from_tree(context, can_gc);
self.bind_unbind(context.tree_is_in_a_document_tree);
}
}

View file

@ -4,7 +4,7 @@
use dom_struct::dom_struct;
use embedder_traits::{EmbedderMsg, LoadStatus};
use html5ever::{LocalName, Prefix, local_name, namespace_url, ns};
use html5ever::{LocalName, Prefix, local_name, ns};
use js::rust::HandleObject;
use servo_url::ServoUrl;
use style::attr::AttrValue;
@ -141,9 +141,9 @@ impl VirtualMethods for HTMLBodyElement {
.attribute_affects_presentational_hints(attr)
}
fn bind_to_tree(&self, context: &BindContext) {
fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
if let Some(s) = self.super_type() {
s.bind_to_tree(context);
s.bind_to_tree(context, can_gc);
}
if !context.tree_is_in_a_document_tree {
@ -176,38 +176,44 @@ impl VirtualMethods for HTMLBodyElement {
}
}
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
let do_super_mutate = match (attr.local_name(), mutation) {
(name, AttributeMutation::Set(_)) if name.starts_with("on") => {
let window = self.owner_window();
// https://html.spec.whatwg.org/multipage/
// #event-handlers-on-elements,-document-objects,-and-window-objects:event-handlers-3
// #event-handlers-on-elements,-document-objects,-and-window-objects:event-handlers-6
match name {
&local_name!("onfocus") |
&local_name!("onload") |
&local_name!("onscroll") |
&local_name!("onafterprint") |
&local_name!("onbeforeprint") |
&local_name!("onbeforeunload") |
&local_name!("onerror") |
&local_name!("onfocus") |
&local_name!("onhashchange") |
&local_name!("onload") |
&local_name!("onlanguagechange") |
&local_name!("onmessage") |
&local_name!("onmessageerror") |
&local_name!("onoffline") |
&local_name!("ononline") |
&local_name!("onpagehide") |
&local_name!("onpagereveal") |
&local_name!("onpageshow") |
&local_name!("onpageswap") |
&local_name!("onpopstate") |
&local_name!("onstorage") |
&local_name!("onrejectionhandled") |
&local_name!("onresize") |
&local_name!("onunload") |
&local_name!("onerror") => {
&local_name!("onscroll") |
&local_name!("onstorage") |
&local_name!("onunhandledrejection") |
&local_name!("onunload") => {
let source = &**attr.value();
let evtarget = window.upcast::<EventTarget>(); // forwarded event
let source_line = 1; //TODO(#9604) obtain current JS execution line
evtarget.set_event_handler_uncompiled(
window.get_url(),
source_line,
&name[2..],
DOMString::from((**attr.value()).to_owned()),
source,
);
false
},
@ -218,7 +224,9 @@ impl VirtualMethods for HTMLBodyElement {
};
if do_super_mutate {
self.super_type().unwrap().attribute_mutated(attr, mutation);
self.super_type()
.unwrap()
.attribute_mutated(attr, mutation, can_gc);
}
}
}

View file

@ -6,7 +6,7 @@ use std::cell::Cell;
use std::default::Default;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix, local_name, namespace_url};
use html5ever::{LocalName, Prefix, local_name};
use js::rust::HandleObject;
use stylo_dom::ElementState;
@ -242,8 +242,10 @@ impl VirtualMethods for HTMLButtonElement {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
self.super_type().unwrap().attribute_mutated(attr, mutation);
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
self.super_type()
.unwrap()
.attribute_mutated(attr, mutation, can_gc);
match *attr.local_name() {
local_name!("disabled") => {
let el = self.upcast::<Element>();
@ -259,9 +261,9 @@ impl VirtualMethods for HTMLButtonElement {
el.check_ancestors_disabled_state_for_form_control();
},
}
el.update_sequentially_focusable_status(CanGc::note());
el.update_sequentially_focusable_status(can_gc);
self.validity_state()
.perform_validation_and_update(ValidationFlags::all());
.perform_validation_and_update(ValidationFlags::all(), can_gc);
},
local_name!("type") => match mutation {
AttributeMutation::Set(_) => {
@ -272,32 +274,32 @@ impl VirtualMethods for HTMLButtonElement {
};
self.button_type.set(value);
self.validity_state()
.perform_validation_and_update(ValidationFlags::all());
.perform_validation_and_update(ValidationFlags::all(), can_gc);
},
AttributeMutation::Removed => {
self.button_type.set(ButtonType::Submit);
},
},
local_name!("form") => {
self.form_attribute_mutated(mutation);
self.form_attribute_mutated(mutation, can_gc);
self.validity_state()
.perform_validation_and_update(ValidationFlags::empty());
.perform_validation_and_update(ValidationFlags::empty(), can_gc);
},
_ => {},
}
}
fn bind_to_tree(&self, context: &BindContext) {
fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
if let Some(s) = self.super_type() {
s.bind_to_tree(context);
s.bind_to_tree(context, can_gc);
}
self.upcast::<Element>()
.check_ancestors_disabled_state_for_form_control();
}
fn unbind_from_tree(&self, context: &UnbindContext) {
self.super_type().unwrap().unbind_from_tree(context);
fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
self.super_type().unwrap().unbind_from_tree(context, can_gc);
let node = self.upcast::<Node>();
let el = self.upcast::<Element>();

View file

@ -7,34 +7,33 @@ use std::collections::HashMap;
use std::rc::Rc;
use canvas_traits::webgl::{GLContextAttributes, WebGLVersion};
use constellation_traits::BlobImpl;
#[cfg(feature = "webgpu")]
use constellation_traits::ScriptToConstellationMessage;
use dom_struct::dom_struct;
use euclid::default::Size2D;
use html5ever::{LocalName, Prefix, local_name, namespace_url, ns};
use html5ever::{LocalName, Prefix, local_name, ns};
use image::codecs::jpeg::JpegEncoder;
use image::codecs::png::PngEncoder;
use image::codecs::webp::WebPEncoder;
use image::{ColorType, ImageEncoder};
use ipc_channel::ipc::IpcSharedMemory;
#[cfg(feature = "webgpu")]
use ipc_channel::ipc::{self as ipcchan};
use js::error::throw_type_error;
use js::rust::{HandleObject, HandleValue};
use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource};
#[cfg(feature = "webgpu")]
use script_traits::ScriptMsg;
use script_traits::serializable::BlobImpl;
use script_layout_interface::HTMLCanvasData;
use servo_media::streams::MediaStreamType;
use servo_media::streams::registry::MediaStreamId;
use snapshot::Snapshot;
use style::attr::AttrValue;
use crate::canvas_context::CanvasContext as _;
pub(crate) use crate::canvas_context::*;
use crate::conversions::Convert;
use crate::dom::attr::Attr;
use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::cell::{DomRefCell, Ref, ref_filter_map};
use crate::dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::{
BlobCallback, HTMLCanvasElementMethods, RenderingContext,
BlobCallback, HTMLCanvasElementMethods, RenderingContext as RootedRenderingContext,
};
use crate::dom::bindings::codegen::Bindings::MediaStreamBinding::MediaStreamMethods;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes;
@ -69,6 +68,7 @@ use crate::script_runtime::{CanGc, JSContext};
const DEFAULT_WIDTH: u32 = 300;
const DEFAULT_HEIGHT: u32 = 150;
#[derive(PartialEq)]
enum EncodedImageType {
Png,
Jpeg,
@ -103,21 +103,14 @@ impl EncodedImageType {
}
}
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
#[derive(Clone, JSTraceable, MallocSizeOf)]
pub(crate) enum CanvasContext {
Placeholder(Dom<OffscreenCanvas>),
Context2d(Dom<CanvasRenderingContext2D>),
WebGL(Dom<WebGLRenderingContext>),
WebGL2(Dom<WebGL2RenderingContext>),
#[cfg(feature = "webgpu")]
WebGPU(Dom<GPUCanvasContext>),
}
/// <https://html.spec.whatwg.org/multipage/#htmlcanvaselement>
#[dom_struct]
pub(crate) struct HTMLCanvasElement {
htmlelement: HTMLElement,
context: DomRefCell<Option<CanvasContext>>,
/// <https://html.spec.whatwg.org/multipage/#concept-canvas-context-mode>
context_mode: DomRefCell<Option<RenderingContext>>,
// This id and hashmap are used to keep track of ongoing toBlob() calls.
callback_id: Cell<u32>,
#[ignore_malloc_size_of = "not implemented for webidl callbacks"]
@ -132,7 +125,7 @@ impl HTMLCanvasElement {
) -> HTMLCanvasElement {
HTMLCanvasElement {
htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
context: DomRefCell::new(None),
context_mode: DomRefCell::new(None),
callback_id: Cell::new(0),
blob_callbacks: RefCell::new(HashMap::new()),
}
@ -157,15 +150,8 @@ impl HTMLCanvasElement {
}
fn recreate_contexts_after_resize(&self) {
if let Some(ref context) = *self.context.borrow() {
match *context {
CanvasContext::Context2d(ref context) => context.resize(),
CanvasContext::WebGL(ref context) => context.resize(),
CanvasContext::WebGL2(ref context) => context.resize(),
#[cfg(feature = "webgpu")]
CanvasContext::WebGPU(ref context) => context.resize(),
CanvasContext::Placeholder(ref context) => context.resize(self.get_size().cast()),
}
if let Some(ref context) = *self.context_mode.borrow() {
context.resize()
}
}
@ -174,24 +160,15 @@ impl HTMLCanvasElement {
}
pub(crate) fn origin_is_clean(&self) -> bool {
match *self.context.borrow() {
Some(CanvasContext::Context2d(ref context)) => context.origin_is_clean(),
match *self.context_mode.borrow() {
Some(ref context) => context.origin_is_clean(),
_ => true,
}
}
pub(crate) fn mark_as_dirty(&self) {
if let Some(ref context) = *self.context.borrow() {
match *context {
CanvasContext::Context2d(ref context) => context.mark_as_dirty(),
CanvasContext::WebGL(ref context) => context.mark_as_dirty(),
CanvasContext::WebGL2(ref context) => context.mark_as_dirty(),
#[cfg(feature = "webgpu")]
CanvasContext::WebGPU(ref context) => context.mark_as_dirty(),
CanvasContext::Placeholder(ref _context) => {
// TODO: Should this be marked as dirty?
},
}
if let Some(ref context) = *self.context_mode.borrow() {
context.mark_as_dirty()
}
}
@ -220,13 +197,15 @@ impl LayoutHTMLCanvasElementHelpers for LayoutDom<'_, HTMLCanvasElement> {
#[allow(unsafe_code)]
fn data(self) -> HTMLCanvasData {
let source = unsafe {
match self.unsafe_get().context.borrow_for_layout().as_ref() {
Some(CanvasContext::Context2d(context)) => context.to_layout().canvas_data_source(),
Some(CanvasContext::WebGL(context)) => context.to_layout().canvas_data_source(),
Some(CanvasContext::WebGL2(context)) => context.to_layout().canvas_data_source(),
match self.unsafe_get().context_mode.borrow_for_layout().as_ref() {
Some(RenderingContext::Context2d(context)) => {
context.to_layout().canvas_data_source()
},
Some(RenderingContext::WebGL(context)) => context.to_layout().canvas_data_source(),
Some(RenderingContext::WebGL2(context)) => context.to_layout().canvas_data_source(),
#[cfg(feature = "webgpu")]
Some(CanvasContext::WebGPU(context)) => context.to_layout().canvas_data_source(),
Some(CanvasContext::Placeholder(_)) | None => HTMLCanvasDataSource::Empty,
Some(RenderingContext::WebGPU(context)) => context.to_layout().canvas_data_source(),
Some(RenderingContext::Placeholder(_)) | None => None,
}
};
@ -245,14 +224,14 @@ impl LayoutHTMLCanvasElementHelpers for LayoutDom<'_, HTMLCanvasElement> {
}
impl HTMLCanvasElement {
pub(crate) fn context(&self) -> Option<Ref<CanvasContext>> {
ref_filter_map(self.context.borrow(), |ctx| ctx.as_ref())
pub(crate) fn context(&self) -> Option<Ref<RenderingContext>> {
ref_filter_map(self.context_mode.borrow(), |ctx| ctx.as_ref())
}
fn get_or_init_2d_context(&self, can_gc: CanGc) -> Option<DomRoot<CanvasRenderingContext2D>> {
if let Some(ctx) = self.context() {
return match *ctx {
CanvasContext::Context2d(ref ctx) => Some(DomRoot::from_ref(ctx)),
RenderingContext::Context2d(ref ctx) => Some(DomRoot::from_ref(ctx)),
_ => None,
};
}
@ -260,7 +239,8 @@ impl HTMLCanvasElement {
let window = self.owner_window();
let size = self.get_size();
let context = CanvasRenderingContext2D::new(window.as_global_scope(), self, size, can_gc);
*self.context.borrow_mut() = Some(CanvasContext::Context2d(Dom::from_ref(&*context)));
*self.context_mode.borrow_mut() =
Some(RenderingContext::Context2d(Dom::from_ref(&*context)));
Some(context)
}
@ -272,7 +252,7 @@ impl HTMLCanvasElement {
) -> Option<DomRoot<WebGLRenderingContext>> {
if let Some(ctx) = self.context() {
return match *ctx {
CanvasContext::WebGL(ref ctx) => Some(DomRoot::from_ref(ctx)),
RenderingContext::WebGL(ref ctx) => Some(DomRoot::from_ref(ctx)),
_ => None,
};
}
@ -288,7 +268,7 @@ impl HTMLCanvasElement {
attrs,
can_gc,
)?;
*self.context.borrow_mut() = Some(CanvasContext::WebGL(Dom::from_ref(&*context)));
*self.context_mode.borrow_mut() = Some(RenderingContext::WebGL(Dom::from_ref(&*context)));
Some(context)
}
@ -304,7 +284,7 @@ impl HTMLCanvasElement {
}
if let Some(ctx) = self.context() {
return match *ctx {
CanvasContext::WebGL2(ref ctx) => Some(DomRoot::from_ref(ctx)),
RenderingContext::WebGL2(ref ctx) => Some(DomRoot::from_ref(ctx)),
_ => None,
};
}
@ -313,7 +293,7 @@ impl HTMLCanvasElement {
let attrs = Self::get_gl_attributes(cx, options)?;
let canvas = HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(self));
let context = WebGL2RenderingContext::new(&window, &canvas, size, attrs, can_gc)?;
*self.context.borrow_mut() = Some(CanvasContext::WebGL2(Dom::from_ref(&*context)));
*self.context_mode.borrow_mut() = Some(RenderingContext::WebGL2(Dom::from_ref(&*context)));
Some(context)
}
@ -323,10 +303,10 @@ impl HTMLCanvasElement {
}
#[cfg(feature = "webgpu")]
fn get_or_init_webgpu_context(&self) -> Option<DomRoot<GPUCanvasContext>> {
fn get_or_init_webgpu_context(&self, can_gc: CanGc) -> Option<DomRoot<GPUCanvasContext>> {
if let Some(ctx) = self.context() {
return match *ctx {
CanvasContext::WebGPU(ref ctx) => Some(DomRoot::from_ref(ctx)),
RenderingContext::WebGPU(ref ctx) => Some(DomRoot::from_ref(ctx)),
_ => None,
};
}
@ -334,22 +314,23 @@ impl HTMLCanvasElement {
let global_scope = self.owner_global();
let _ = global_scope
.script_to_constellation_chan()
.send(ScriptMsg::GetWebGPUChan(sender));
.send(ScriptToConstellationMessage::GetWebGPUChan(sender));
receiver
.recv()
.expect("Failed to get WebGPU channel")
.map(|channel| {
let context = GPUCanvasContext::new(&global_scope, self, channel, CanGc::note());
*self.context.borrow_mut() = Some(CanvasContext::WebGPU(Dom::from_ref(&*context)));
let context = GPUCanvasContext::new(&global_scope, self, channel, can_gc);
*self.context_mode.borrow_mut() =
Some(RenderingContext::WebGPU(Dom::from_ref(&*context)));
context
})
}
/// Gets the base WebGLRenderingContext for WebGL or WebGL 2, if exists.
pub(crate) fn get_base_webgl_context(&self) -> Option<DomRoot<WebGLRenderingContext>> {
match *self.context.borrow() {
Some(CanvasContext::WebGL(ref context)) => Some(DomRoot::from_ref(context)),
Some(CanvasContext::WebGL2(ref context)) => Some(context.base_context()),
match *self.context_mode.borrow() {
Some(RenderingContext::WebGL(ref context)) => Some(DomRoot::from_ref(context)),
Some(RenderingContext::WebGL2(ref context)) => Some(context.base_context()),
_ => None,
}
}
@ -375,42 +356,16 @@ impl HTMLCanvasElement {
self.Height() != 0 && self.Width() != 0
}
pub(crate) fn fetch_all_data(&self) -> Option<(Option<IpcSharedMemory>, Size2D<u32>)> {
let size = self.get_size();
if size.width == 0 || size.height == 0 {
return None;
}
let data = match self.context.borrow().as_ref() {
Some(CanvasContext::Context2d(context)) => context.get_image_data_as_shared_memory(),
Some(CanvasContext::WebGL(_context)) => {
// TODO: add a method in WebGLRenderingContext to get the pixels.
return None;
},
Some(CanvasContext::WebGL2(_context)) => {
// TODO: add a method in WebGL2RenderingContext to get the pixels.
return None;
},
#[cfg(feature = "webgpu")]
Some(CanvasContext::WebGPU(context)) => context.get_image_data_as_shared_memory(),
Some(CanvasContext::Placeholder(context)) => return context.fetch_all_data(),
None => None,
};
Some((data, size))
}
fn get_content(&self) -> Option<Vec<u8>> {
match *self.context.borrow() {
Some(CanvasContext::Context2d(ref context)) => context.get_image_data(),
Some(CanvasContext::WebGL(ref context)) => context.get_image_data(),
Some(CanvasContext::WebGL2(ref context)) => context.get_image_data(),
#[cfg(feature = "webgpu")]
Some(CanvasContext::WebGPU(ref context)) => context.get_image_data(),
Some(CanvasContext::Placeholder(_)) | None => {
// Each pixel is fully-transparent black.
Some(vec![0; (self.Width() * self.Height() * 4) as usize])
pub(crate) fn get_image_data(&self) -> Option<Snapshot> {
match self.context_mode.borrow().as_ref() {
Some(context) => context.get_image_data(),
None => {
let size = self.get_size();
if size.width == 0 || size.height == 0 {
None
} else {
Some(Snapshot::cleared(size.cast()))
}
},
}
}
@ -427,15 +382,23 @@ impl HTMLCanvasElement {
&self,
image_type: &EncodedImageType,
quality: Option<f64>,
bytes: &[u8],
snapshot: &Snapshot,
encoder: &mut W,
) {
// We can't use self.Width() or self.Height() here, since the size of the canvas
// may have changed since the snapshot was created. Truncating the dimensions to a
// u32 can't panic, since the data comes from a canvas which is always smaller than
// u32::MAX.
let canvas_data = snapshot.data();
let width = snapshot.size().width as u32;
let height = snapshot.size().height as u32;
match image_type {
EncodedImageType::Png => {
// FIXME(nox): https://github.com/image-rs/image-png/issues/86
// FIXME(nox): https://github.com/image-rs/image-png/issues/87
PngEncoder::new(encoder)
.write_image(bytes, self.Width(), self.Height(), ColorType::Rgba8)
.write_image(canvas_data, width, height, ColorType::Rgba8)
.unwrap();
},
EncodedImageType::Jpeg => {
@ -455,14 +418,14 @@ impl HTMLCanvasElement {
};
jpeg_encoder
.write_image(bytes, self.Width(), self.Height(), ColorType::Rgba8)
.write_image(canvas_data, width, height, ColorType::Rgba8)
.unwrap();
},
EncodedImageType::Webp => {
// No quality support because of https://github.com/image-rs/image/issues/1984
WebPEncoder::new_lossless(encoder)
.write_image(bytes, self.Width(), self.Height(), ColorType::Rgba8)
.write_image(canvas_data, width, height, ColorType::Rgba8)
.unwrap();
},
}
@ -473,12 +436,12 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
// https://html.spec.whatwg.org/multipage/#dom-canvas-width
make_uint_getter!(Width, "width", DEFAULT_WIDTH);
// https://html.spec.whatwg.org/multipage/#dom-canvas-width
// When setting the value of the width or height attribute, if the context mode of the canvas element
// is set to placeholder, the user agent must throw an "InvalidStateError" DOMException and leave the
// attribute's value unchanged.
fn SetWidth(&self, value: u32) -> Fallible<()> {
if let Some(CanvasContext::Placeholder(_)) = *self.context.borrow() {
/// <https://html.spec.whatwg.org/multipage/#dom-canvas-width>
fn SetWidth(&self, value: u32, can_gc: CanGc) -> Fallible<()> {
// > When setting the value of the width or height attribute, if the context mode of the canvas element
// > is set to placeholder, the user agent must throw an "InvalidStateError" DOMException and leave the
// > attribute's value unchanged.
if let Some(RenderingContext::Placeholder(_)) = *self.context_mode.borrow() {
return Err(Error::InvalidState);
}
@ -488,16 +451,19 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
value
};
let element = self.upcast::<Element>();
element.set_uint_attribute(&html5ever::local_name!("width"), value, CanGc::note());
element.set_uint_attribute(&html5ever::local_name!("width"), value, can_gc);
Ok(())
}
// https://html.spec.whatwg.org/multipage/#dom-canvas-height
make_uint_getter!(Height, "height", DEFAULT_HEIGHT);
// https://html.spec.whatwg.org/multipage/#dom-canvas-height
fn SetHeight(&self, value: u32) -> Fallible<()> {
if let Some(CanvasContext::Placeholder(_)) = *self.context.borrow() {
/// <https://html.spec.whatwg.org/multipage/#dom-canvas-height>
fn SetHeight(&self, value: u32, can_gc: CanGc) -> Fallible<()> {
// > When setting the value of the width or height attribute, if the context mode of the canvas element
// > is set to placeholder, the user agent must throw an "InvalidStateError" DOMException and leave the
// > attribute's value unchanged.
if let Some(RenderingContext::Placeholder(_)) = *self.context_mode.borrow() {
return Err(Error::InvalidState);
}
@ -507,7 +473,7 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
value
};
let element = self.upcast::<Element>();
element.set_uint_attribute(&html5ever::local_name!("height"), value, CanGc::note());
element.set_uint_attribute(&html5ever::local_name!("height"), value, can_gc);
Ok(())
}
@ -518,26 +484,26 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
id: DOMString,
options: HandleValue,
can_gc: CanGc,
) -> Fallible<Option<RenderingContext>> {
) -> Fallible<Option<RootedRenderingContext>> {
// Always throw an InvalidState exception when the canvas is in Placeholder mode (See table in the spec).
if let Some(CanvasContext::Placeholder(_)) = *self.context.borrow() {
if let Some(RenderingContext::Placeholder(_)) = *self.context_mode.borrow() {
return Err(Error::InvalidState);
}
Ok(match &*id {
"2d" => self
.get_or_init_2d_context(can_gc)
.map(RenderingContext::CanvasRenderingContext2D),
.map(RootedRenderingContext::CanvasRenderingContext2D),
"webgl" | "experimental-webgl" => self
.get_or_init_webgl_context(cx, options, can_gc)
.map(RenderingContext::WebGLRenderingContext),
.map(RootedRenderingContext::WebGLRenderingContext),
"webgl2" | "experimental-webgl2" => self
.get_or_init_webgl2_context(cx, options, can_gc)
.map(RenderingContext::WebGL2RenderingContext),
.map(RootedRenderingContext::WebGL2RenderingContext),
#[cfg(feature = "webgpu")]
"webgpu" => self
.get_or_init_webgpu_context()
.map(RenderingContext::GPUCanvasContext),
.get_or_init_webgpu_context(can_gc)
.map(RootedRenderingContext::GPUCanvasContext),
_ => None,
})
}
@ -560,11 +526,23 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
}
// Step 3.
let Some(file) = self.get_content() else {
let Some(mut snapshot) = self.get_image_data() else {
return Ok(USVString("data:,".into()));
};
let image_type = EncodedImageType::from(mime_type);
snapshot.transform(
if image_type == EncodedImageType::Jpeg {
snapshot::AlphaMode::AsOpaque {
premultiplied: true,
}
} else {
snapshot::AlphaMode::Transparent {
premultiplied: false,
}
},
snapshot::PixelFormat::RGBA,
);
let mut url = format!("data:{};base64,", image_type.as_mime_type());
let mut encoder = base64::write::EncoderStringWriter::from_consumer(
@ -575,7 +553,7 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
self.encode_for_mime_type(
&image_type,
Self::maybe_quality(quality),
&file,
&snapshot,
&mut encoder,
);
encoder.into_inner();
@ -597,14 +575,14 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
return Err(Error::Security);
}
// Step 2. and 3.
// If this canvas element's bitmap has pixels (i.e., neither its horizontal dimension
// Step 2. Let result be null.
// Step 3. If this canvas element's bitmap has pixels (i.e., neither its horizontal dimension
// nor its vertical dimension is zero),
// then set result to a copy of this canvas element's bitmap.
let result = if self.Width() == 0 || self.Height() == 0 {
None
} else {
self.get_content()
self.get_image_data()
};
let this = Trusted::new(self);
@ -625,18 +603,22 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
return error!("Expected blob callback, but found none!");
};
if let Some(bytes) = result {
if let Some(mut snapshot) = result {
snapshot.transform(
snapshot::AlphaMode::Transparent{ premultiplied: false },
snapshot::PixelFormat::RGBA
);
// Step 4.1
// If result is non-null, then set result to a serialization of result as a file with
// type and quality if given.
let mut encoded: Vec<u8> = vec![];
this.encode_for_mime_type(&image_type, quality, &bytes, &mut encoded);
this.encode_for_mime_type(&image_type, quality, &snapshot, &mut encoded);
let blob_impl = BlobImpl::new_from_bytes(encoded, image_type.as_mime_type());
// Step 4.2.1 & 4.2.2
// Set result to a new Blob object, created in the relevant realm of this canvas element
// Invoke callback with « result » and "report".
// Step 4.2.1 Set result to a new Blob object, created in the relevant realm of this canvas element
let blob = Blob::new(&this.global(), blob_impl, CanGc::note());
// Step 4.2.2 Invoke callback with « result » and "report".
let _ = callback.Call__(Some(&blob), ExceptionHandling::Report, CanGc::note());
} else {
let _ = callback.Call__(None, ExceptionHandling::Report, CanGc::note());
@ -647,8 +629,8 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
}
/// <https://html.spec.whatwg.org/multipage/#dom-canvas-transfercontroltooffscreen>
fn TransferControlToOffscreen(&self) -> Fallible<DomRoot<OffscreenCanvas>> {
if self.context.borrow().is_some() {
fn TransferControlToOffscreen(&self, can_gc: CanGc) -> Fallible<DomRoot<OffscreenCanvas>> {
if self.context_mode.borrow().is_some() {
// Step 1.
// If this canvas element's context mode is not set to none, throw an "InvalidStateError" DOMException.
return Err(Error::InvalidState);
@ -665,10 +647,12 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
self.Width().into(),
self.Height().into(),
Some(&Dom::from_ref(self)),
CanGc::note(),
can_gc,
);
// Step 4. Set this canvas element's context mode to placeholder.
*self.context.borrow_mut() = Some(CanvasContext::Placeholder(offscreen_canvas.as_traced()));
*self.context_mode.borrow_mut() =
Some(RenderingContext::Placeholder(offscreen_canvas.as_traced()));
// Step 5. Return offscreenCanvas.
Ok(offscreen_canvas)
@ -698,8 +682,10 @@ impl VirtualMethods for HTMLCanvasElement {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
self.super_type().unwrap().attribute_mutated(attr, mutation);
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
self.super_type()
.unwrap()
.attribute_mutated(attr, mutation, can_gc);
match attr.local_name() {
&local_name!("width") | &local_name!("height") => self.recreate_contexts_after_resize(),
_ => (),

View file

@ -87,7 +87,7 @@ impl HTMLCollection {
}
/// Returns a collection which is always empty.
pub(crate) fn always_empty(window: &Window, root: &Node) -> DomRoot<Self> {
pub(crate) fn always_empty(window: &Window, root: &Node, can_gc: CanGc) -> DomRoot<Self> {
#[derive(JSTraceable)]
struct NoFilter;
impl CollectionFilter for NoFilter {
@ -96,7 +96,7 @@ impl HTMLCollection {
}
}
Self::new(window, root, Box::new(NoFilter), CanGc::note())
Self::new(window, root, Box::new(NoFilter), can_gc)
}
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
@ -114,6 +114,7 @@ impl HTMLCollection {
window: &Window,
root: &Node,
filter_function: fn(&Element, &Node) -> bool,
can_gc: CanGc,
) -> DomRoot<Self> {
#[derive(JSTraceable, MallocSizeOf)]
pub(crate) struct StaticFunctionFilter(
@ -132,7 +133,7 @@ impl HTMLCollection {
window,
root,
Box::new(StaticFunctionFilter(filter_function)),
CanGc::note(),
can_gc,
)
}
@ -140,8 +141,9 @@ impl HTMLCollection {
window: &Window,
root: &Node,
filter: Box<dyn CollectionFilter + 'static>,
can_gc: CanGc,
) -> DomRoot<Self> {
Self::new(window, root, filter, CanGc::note())
Self::new(window, root, filter, can_gc)
}
fn validate_cache(&self) {
@ -176,6 +178,7 @@ impl HTMLCollection {
window: &Window,
root: &Node,
qualified_name: LocalName,
can_gc: CanGc,
) -> DomRoot<HTMLCollection> {
// case 1
if qualified_name == local_name!("*") {
@ -186,7 +189,7 @@ impl HTMLCollection {
true
}
}
return HTMLCollection::create(window, root, Box::new(AllFilter));
return HTMLCollection::create(window, root, Box::new(AllFilter), can_gc);
}
#[derive(JSTraceable, MallocSizeOf)]
@ -212,7 +215,7 @@ impl HTMLCollection {
ascii_lower_qualified_name: qualified_name.to_ascii_lowercase(),
qualified_name,
};
HTMLCollection::create(window, root, Box::new(filter))
HTMLCollection::create(window, root, Box::new(filter), can_gc)
}
fn match_element(elem: &Element, qualified_name: &LocalName) -> bool {
@ -231,17 +234,19 @@ impl HTMLCollection {
root: &Node,
tag: DOMString,
maybe_ns: Option<DOMString>,
can_gc: CanGc,
) -> DomRoot<HTMLCollection> {
let local = LocalName::from(tag);
let ns = namespace_from_domstring(maybe_ns);
let qname = QualName::new(None, ns, local);
HTMLCollection::by_qual_tag_name(window, root, qname)
HTMLCollection::by_qual_tag_name(window, root, qname, can_gc)
}
pub(crate) fn by_qual_tag_name(
window: &Window,
root: &Node,
qname: QualName,
can_gc: CanGc,
) -> DomRoot<HTMLCollection> {
#[derive(JSTraceable, MallocSizeOf)]
struct TagNameNSFilter {
@ -256,22 +261,24 @@ impl HTMLCollection {
}
}
let filter = TagNameNSFilter { qname };
HTMLCollection::create(window, root, Box::new(filter))
HTMLCollection::create(window, root, Box::new(filter), can_gc)
}
pub(crate) fn by_class_name(
window: &Window,
root: &Node,
classes: DOMString,
can_gc: CanGc,
) -> DomRoot<HTMLCollection> {
let class_atoms = split_html_space_chars(&classes).map(Atom::from).collect();
HTMLCollection::by_atomic_class_name(window, root, class_atoms)
HTMLCollection::by_atomic_class_name(window, root, class_atoms, can_gc)
}
pub(crate) fn by_atomic_class_name(
window: &Window,
root: &Node,
classes: Vec<Atom>,
can_gc: CanGc,
) -> DomRoot<HTMLCollection> {
#[derive(JSTraceable, MallocSizeOf)]
struct ClassNameFilter {
@ -292,17 +299,20 @@ impl HTMLCollection {
}
if classes.is_empty() {
return HTMLCollection::always_empty(window, root);
return HTMLCollection::always_empty(window, root, can_gc);
}
let filter = ClassNameFilter { classes };
HTMLCollection::create(window, root, Box::new(filter))
HTMLCollection::create(window, root, Box::new(filter), can_gc)
}
pub(crate) fn children(window: &Window, root: &Node) -> DomRoot<HTMLCollection> {
HTMLCollection::new_with_filter_fn(window, root, |element, root| {
root.is_parent_of(element.upcast())
})
pub(crate) fn children(window: &Window, root: &Node, can_gc: CanGc) -> DomRoot<HTMLCollection> {
HTMLCollection::new_with_filter_fn(
window,
root,
|element, root| root.is_parent_of(element.upcast()),
can_gc,
)
}
pub(crate) fn elements_iter_after<'a>(

View file

@ -53,9 +53,12 @@ impl HTMLDataListElement {
impl HTMLDataListElementMethods<crate::DomTypeHolder> for HTMLDataListElement {
// https://html.spec.whatwg.org/multipage/#dom-datalist-options
fn Options(&self) -> DomRoot<HTMLCollection> {
HTMLCollection::new_with_filter_fn(&self.owner_window(), self.upcast(), |element, _| {
element.is::<HTMLOptionElement>()
})
fn Options(&self, can_gc: CanGc) -> DomRoot<HTMLCollection> {
HTMLCollection::new_with_filter_fn(
&self.owner_window(),
self.upcast(),
|element, _| element.is::<HTMLOptionElement>(),
can_gc,
)
}
}

View file

@ -118,7 +118,7 @@ impl HTMLDetailsElement {
let summary = HTMLSlotElement::new(local_name!("slot"), None, &document, None, can_gc);
root.upcast::<Node>()
.AppendChild(summary.upcast::<Node>())
.AppendChild(summary.upcast::<Node>(), can_gc)
.unwrap();
let fallback_summary =
@ -128,12 +128,12 @@ impl HTMLDetailsElement {
.SetTextContent(Some(DEFAULT_SUMMARY.into()), can_gc);
summary
.upcast::<Node>()
.AppendChild(fallback_summary.upcast::<Node>())
.AppendChild(fallback_summary.upcast::<Node>(), can_gc)
.unwrap();
let descendants = HTMLSlotElement::new(local_name!("slot"), None, &document, None, can_gc);
root.upcast::<Node>()
.AppendChild(descendants.upcast::<Node>())
.AppendChild(descendants.upcast::<Node>(), can_gc)
.unwrap();
let _ = self.shadow_tree.borrow_mut().insert(ShadowTree {
@ -178,8 +178,6 @@ impl HTMLDetailsElement {
}
}
shadow_tree.descendants.Assign(slottable_children);
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
}
fn update_shadow_tree_styles(&self, can_gc: CanGc) {
@ -214,8 +212,6 @@ impl HTMLDetailsElement {
.implicit_summary
.upcast::<Element>()
.set_string_attribute(&local_name!("style"), implicit_summary_style.into(), can_gc);
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
}
}
@ -232,11 +228,13 @@ impl VirtualMethods for HTMLDetailsElement {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
self.super_type().unwrap().attribute_mutated(attr, mutation);
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
self.super_type()
.unwrap()
.attribute_mutated(attr, mutation, can_gc);
if attr.local_name() == &local_name!("open") {
self.update_shadow_tree_styles(CanGc::note());
self.update_shadow_tree_styles(can_gc);
let counter = self.toggle_counter.get() + 1;
self.toggle_counter.set(counter);
@ -261,8 +259,8 @@ impl VirtualMethods for HTMLDetailsElement {
self.update_shadow_tree_contents(CanGc::note());
}
fn bind_to_tree(&self, context: &BindContext) {
self.super_type().unwrap().bind_to_tree(context);
fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
self.super_type().unwrap().bind_to_tree(context, can_gc);
self.update_shadow_tree_contents(CanGc::note());
self.update_shadow_tree_styles(CanGc::note());

View file

@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix, local_name, namespace_url, ns};
use html5ever::{LocalName, Prefix, local_name, ns};
use js::rust::HandleObject;
use crate::dom::bindings::cell::DomRefCell;
@ -99,13 +99,13 @@ impl HTMLDialogElementMethods<crate::DomTypeHolder> for HTMLDialogElement {
}
// https://html.spec.whatwg.org/multipage/#dom-dialog-close
fn Close(&self, return_value: Option<DOMString>) {
fn Close(&self, return_value: Option<DOMString>, can_gc: CanGc) {
let element = self.upcast::<Element>();
let target = self.upcast::<EventTarget>();
// Step 1 & 2
if element
.remove_attribute(&ns!(), &local_name!("open"))
.remove_attribute(&ns!(), &local_name!("open"), can_gc)
.is_none()
{
return;

Some files were not shown because too many files have changed in this diff Show more