mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Merge branch 'main' into outreachy-intern
Signed-off-by: Aniebiet Afia <54312052+aniebietafia@users.noreply.github.com>
This commit is contained in:
commit
2e9bc76b7d
5603 changed files with 188713 additions and 83849 deletions
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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
|
||||
// ===================================================
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
|
@ -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(())
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
|
@ -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())
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
};
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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>>;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>>;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
282
components/script/dom/clipboard.rs
Normal file
282
components/script/dom/clipboard.rs
Normal 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 v’s 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 realm’s 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 realm’s 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 realm’s 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 realm’s 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 systemClipboardRepresentation’s 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 representation’s MIME type essence is "text/plain", then:
|
||||
|
||||
// Step 3.4.1.1.4.1 Set representation’s MIME type to mimeType.
|
||||
// Note: Done when creating a new representation
|
||||
|
||||
// Step 3.4.1.1.4.2 Let representationDataPromise be the representation’s 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 item’s type.
|
||||
|
||||
// Step 2.2 If formatString is empty then follow the below steps:
|
||||
|
||||
// Step 2.2.1 Let webCustomFormatString be the item’s 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 webCustomFormat’s type's essence equal to webMimeType.
|
||||
// Step 2.2.7 Set item’s type to webCustomFormat.
|
||||
// Step 2.2.8 Append webCustomFormat to webCustomFormats.
|
||||
|
||||
// Step 2.3 Let payload be the result of UTF-8 decoding item’s 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
|
||||
}
|
|
@ -41,7 +41,7 @@ impl ClipboardEventType {
|
|||
}
|
||||
|
||||
#[dom_struct]
|
||||
pub struct ClipboardEvent {
|
||||
pub(crate) struct ClipboardEvent {
|
||||
event: Event,
|
||||
clipboard_data: MutNullableDom<DataTransfer>,
|
||||
}
|
||||
|
|
181
components/script/dom/clipboarditem.rs
Normal file
181
components/script/dom/clipboarditem.rs
Normal 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 representation’s isCustom flag to isCustom.
|
||||
// Step 6.8 Set representation’s MIME type to mimeType.
|
||||
// Step 6.9 Set representation’s 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,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
})
|
||||
|
|
|
@ -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 {
|
||||
|
|
91
components/script/dom/cssnesteddeclarations.rs
Normal file
91
components/script/dom/cssnesteddeclarations.rs
Normal 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,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
))
|
||||
},
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 stylesheet’s 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 sheet’s CSS rules to rules.
|
||||
// We reset our rule list, which will be initialized properly
|
||||
// at the next getter access.
|
||||
self.rulelist.set(None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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![];
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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,
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
// activationTarget’s 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 event’s currentTarget attribute value’s 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 struct’s 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 event’s type attribute value is not listener’s type, then continue.
|
||||
// Step 2.1 If event’s type attribute value is not listener’s type, then continue.
|
||||
|
||||
// Step 2.2. Set found to true.
|
||||
found = true;
|
||||
|
||||
// TODO Step 2.3 If phase is "capturing" and listener’s capture is false, then continue.
|
||||
// TODO Step 2.4 If phase is "bubbling" and listener’s capture is true, then continue.
|
||||
// Step 2.3 If phase is "capturing" and listener’s capture is false, then continue.
|
||||
// Step 2.4 If phase is "bubbling" and listener’s 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 listener’s once is true, then remove an event listener given event’s 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 callback’s associated realm’s 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 listener’s 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 object’s operation with listener’s callback, "handleEvent", « event »,
|
||||
// and event’s currentTarget attribute value. If this throws an exception exception:
|
||||
// Step 2.10.1 Report exception for listener’s callback’s corresponding JavaScript object’s
|
||||
// associated realm’s 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());
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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(),
|
||||
_ => (),
|
||||
|
|
|
@ -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>(
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue