mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Move generated bindings to script_bindings (#36323)
This is the final step of #1799, where the majority of the generated code for the JS bindings is now compiled as part of the script_bindings build step. The remaining pieces in script must live there because they refer to concrete DOM types; all code in script_bindings is generic over the [DomTypes](https://doc.servo.org/script/dom/bindings/codegen/DomTypes/trait.DomTypes.html) trait. My testing with incremental builds shows me a 12 second reduction in build times on my 2024 M4 Macbook Pro when modifying code in the script crate after these changes. Before this PR those changes took 20 seconds to rebuild Servo, and now they take 8 seconds. Testing: Existing WPT tests ensure no regressions. Fixes: #1799 --------- Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
parent
277c0b82dd
commit
b4079b3ff3
44 changed files with 1932 additions and 1829 deletions
|
@ -19,6 +19,9 @@ path = "lib.rs"
|
|||
bitflags = { workspace = true }
|
||||
crossbeam-channel = { workspace = true }
|
||||
cssparser = { workspace = true }
|
||||
deny_public_fields = { path = "../deny_public_fields" }
|
||||
dom_struct = { path = "../dom_struct" }
|
||||
domobject_derive = { path = "../domobject_derive" }
|
||||
html5ever = { workspace = true }
|
||||
indexmap = { workspace = true }
|
||||
js = { workspace = true }
|
||||
|
@ -29,13 +32,16 @@ malloc_size_of = { workspace = true }
|
|||
malloc_size_of_derive = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
parking_lot = { workspace = true }
|
||||
phf = "0.11"
|
||||
regex = { workspace = true }
|
||||
servo_arc = { workspace = true }
|
||||
servo_config = { path = "../config" }
|
||||
servo_url = { path = "../url" }
|
||||
smallvec = { workspace = true }
|
||||
stylo = { workspace = true }
|
||||
stylo_atoms = { workspace = true }
|
||||
tendril = { version = "0.4.1", features = ["encoding_rs"] }
|
||||
tracing = { workspace = true, optional = true }
|
||||
webxr-api = { workspace = true, optional = true }
|
||||
xml5ever = { workspace = true }
|
||||
|
||||
|
@ -46,6 +52,7 @@ serde_json = { workspace = true }
|
|||
|
||||
[features]
|
||||
bluetooth = []
|
||||
tracing = ["dep:tracing"]
|
||||
webgpu = []
|
||||
webxr = ["webxr-api"]
|
||||
|
||||
|
|
|
@ -2,10 +2,31 @@
|
|||
* 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 js::jsapi::JSObject;
|
||||
use js::rust::HandleObject;
|
||||
//! 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::{HandleObject, MutableHandleValue, Runtime};
|
||||
|
||||
use crate::DomTypes;
|
||||
use crate::codegen::GenericBindings::WindowBinding::Window_Binding::WindowMethods;
|
||||
use crate::error::{Error, Fallible};
|
||||
use crate::inheritance::Castable;
|
||||
use crate::interfaces::{DocumentHelpers, DomHelpers, GlobalScopeHelpers};
|
||||
use crate::realms::{InRealm, enter_realm};
|
||||
use crate::reflector::DomObject;
|
||||
use crate::root::{Dom, DomRoot};
|
||||
use crate::script_runtime::{CanGc, JSContext};
|
||||
use crate::settings_stack::{GenericAutoEntryScript, GenericAutoIncumbentScript};
|
||||
use crate::utils::AsCCharPtrPtr;
|
||||
|
||||
pub trait ThisReflector {
|
||||
fn jsobject(&self) -> *mut JSObject;
|
||||
|
@ -22,3 +43,275 @@ impl ThisReflector for HandleObject<'_> {
|
|||
self.get()
|
||||
}
|
||||
}
|
||||
|
||||
/// The exception handling used for a call.
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub 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 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 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 trait CallbackContainer<D: DomTypes> {
|
||||
/// Create a new CallbackContainer object for the given `JSObject`.
|
||||
///
|
||||
/// # Safety
|
||||
/// `callback` must point to a valid, non-null 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 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 fn new() -> Self {
|
||||
Self {
|
||||
object: CallbackObject::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying `CallbackObject`.
|
||||
pub fn callback_holder(&self) -> &CallbackObject<D> {
|
||||
&self.object
|
||||
}
|
||||
|
||||
/// Initialize the callback function with a value.
|
||||
/// Should be called once this object is done moving.
|
||||
///
|
||||
/// # Safety
|
||||
/// `callback` must point to a valid, non-null JSObject.
|
||||
pub 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 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 fn new() -> Self {
|
||||
Self {
|
||||
object: CallbackObject::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying `CallbackObject`.
|
||||
pub fn callback_holder(&self) -> &CallbackObject<D> {
|
||||
&self.object
|
||||
}
|
||||
|
||||
/// Initialize the callback function with a value.
|
||||
/// Should be called once this object is done moving.
|
||||
///
|
||||
/// # Safety
|
||||
/// `callback` must point to a valid, non-null JSObject.
|
||||
pub 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 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())
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 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 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 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::<D>(&*self.exception_global);
|
||||
<D as DomHelpers<D>>::report_pending_exception(
|
||||
self.cx,
|
||||
true,
|
||||
InRealm::Entered(&ar),
|
||||
CanGc::note(),
|
||||
);
|
||||
}
|
||||
drop(self.incumbent_script.take());
|
||||
drop(self.entry_script.take().unwrap());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ DOMInterfaces = {
|
|||
},
|
||||
|
||||
'Document': {
|
||||
'additionalTraits': ["script_bindings::interfaces::DocumentHelpers"],
|
||||
'additionalTraits': ["crate::interfaces::DocumentHelpers"],
|
||||
'canGc': ['Close', 'CreateElement', 'CreateElementNS', 'ImportNode', 'SetTitle', 'Write', 'Writeln', 'CreateEvent', 'CreateRange', 'Open', 'Open_', 'CreateComment', 'CreateAttribute', 'CreateAttributeNS', 'CreateDocumentFragment', 'CreateTextNode', 'CreateCDATASection', 'CreateProcessingInstruction', 'Prepend', 'Append', 'ReplaceChildren', 'SetBgColor', 'SetFgColor', 'Fonts', 'ElementFromPoint', 'ElementsFromPoint', 'ExitFullscreen', 'CreateExpression', 'CreateNSResolver', 'Evaluate'],
|
||||
},
|
||||
|
||||
|
@ -240,7 +240,7 @@ DOMInterfaces = {
|
|||
},
|
||||
|
||||
'GlobalScope': {
|
||||
'additionalTraits': ['crate::dom::globalscope::GlobalScopeHelpers<Self>'],
|
||||
'additionalTraits': ['crate::interfaces::GlobalScopeHelpers<Self>'],
|
||||
},
|
||||
|
||||
'GPU': {
|
||||
|
@ -494,7 +494,7 @@ DOMInterfaces = {
|
|||
|
||||
'Promise': {
|
||||
'spiderMonkeyInterface': True,
|
||||
'additionalTraits': ["crate::dom::promise::PromiseHelpers<Self>", "js::conversions::FromJSValConvertibleRc"]
|
||||
'additionalTraits': ["crate::interfaces::PromiseHelpers<Self>", "js::conversions::FromJSValConvertibleRc"]
|
||||
},
|
||||
|
||||
'Range': {
|
||||
|
@ -531,7 +531,7 @@ DOMInterfaces = {
|
|||
'ServoInternals': {
|
||||
'inRealms': ['ReportMemory'],
|
||||
'canGc': ['ReportMemory'],
|
||||
'additionalTraits': ['script_bindings::interfaces::ServoInternalsHelpers'],
|
||||
'additionalTraits': ['crate::interfaces::ServoInternalsHelpers'],
|
||||
},
|
||||
|
||||
'ShadowRoot': {
|
||||
|
@ -555,7 +555,7 @@ DOMInterfaces = {
|
|||
'TestBinding': {
|
||||
'inRealms': ['PromiseAttribute', 'PromiseNativeHandler'],
|
||||
'canGc': ['InterfaceAttribute', 'GetInterfaceAttributeNullable', 'ReceiveInterface', 'ReceiveInterfaceSequence', 'ReceiveNullableInterface', 'PromiseAttribute', 'PromiseNativeHandler', 'PromiseResolveNative', 'PromiseRejectNative', 'PromiseRejectWithTypeError'],
|
||||
'additionalTraits': ['script_bindings::interfaces::TestBindingHelpers'],
|
||||
'additionalTraits': ['crate::interfaces::TestBindingHelpers'],
|
||||
},
|
||||
|
||||
'TestWorklet': {
|
||||
|
@ -586,13 +586,13 @@ DOMInterfaces = {
|
|||
|
||||
'WebGL2RenderingContext': {
|
||||
'canGc': ['MakeXRCompatible'],
|
||||
'additionalTraits': ['script_bindings::interfaces::WebGL2RenderingContextHelpers'],
|
||||
'additionalTraits': ['crate::interfaces::WebGL2RenderingContextHelpers'],
|
||||
},
|
||||
|
||||
'Window': {
|
||||
'canGc': ['Stop', 'Fetch', 'Scroll', 'Scroll_','ScrollBy', 'ScrollBy_', 'Stop', 'Fetch', 'Open', 'CreateImageBitmap'],
|
||||
'inRealms': ['Fetch', 'GetOpener'],
|
||||
'additionalTraits': ['script_bindings::interfaces::WindowHelpers'],
|
||||
'additionalTraits': ['crate::interfaces::WindowHelpers'],
|
||||
},
|
||||
|
||||
'WindowProxy' : {
|
||||
|
|
|
@ -2282,10 +2282,10 @@ class CGImports(CGWrapper):
|
|||
parentName = descriptor.getParentName()
|
||||
while parentName:
|
||||
descriptor = descriptorProvider.getDescriptor(parentName)
|
||||
extras += [descriptor.path, descriptor.bindingPath]
|
||||
extras += [descriptor.bindingPath]
|
||||
parentName = descriptor.getParentName()
|
||||
elif t.isType() and t.isRecord():
|
||||
extras += ['script_bindings::record::Record']
|
||||
extras += ['crate::record::Record']
|
||||
elif isinstance(t, IDLPromiseType):
|
||||
pass
|
||||
else:
|
||||
|
@ -2316,7 +2316,7 @@ class CGTemplatedType(CGWrapper):
|
|||
|
||||
class CGNamespace(CGWrapper):
|
||||
def __init__(self, namespace, child, public=False):
|
||||
pub = "pub(crate) " if public else ""
|
||||
pub = "pub " if public else ""
|
||||
pre = f"{pub}mod {namespace} {{\n"
|
||||
post = f"}} // mod {namespace}"
|
||||
CGWrapper.__init__(self, child, pre=pre, post=post)
|
||||
|
@ -2337,15 +2337,15 @@ def DOMClassTypeId(desc):
|
|||
inner = ""
|
||||
if desc.hasDescendants():
|
||||
if desc.interface.getExtendedAttribute("Abstract"):
|
||||
return "script_bindings::codegen::InheritTypes::TopTypeId { abstract_: () }"
|
||||
return "crate::codegen::InheritTypes::TopTypeId { abstract_: () }"
|
||||
name = desc.interface.identifier.name
|
||||
inner = f"(script_bindings::codegen::InheritTypes::{name}TypeId::{name})"
|
||||
inner = f"(crate::codegen::InheritTypes::{name}TypeId::{name})"
|
||||
elif len(protochain) == 1:
|
||||
return "script_bindings::codegen::InheritTypes::TopTypeId { alone: () }"
|
||||
return "crate::codegen::InheritTypes::TopTypeId { alone: () }"
|
||||
reversed_protochain = list(reversed(protochain))
|
||||
for (child, parent) in zip(reversed_protochain, reversed_protochain[1:]):
|
||||
inner = f"(script_bindings::codegen::InheritTypes::{parent}TypeId::{child}{inner})"
|
||||
return f"script_bindings::codegen::InheritTypes::TopTypeId {{ {protochain[0].lower()}: {inner} }}"
|
||||
inner = f"(crate::codegen::InheritTypes::{parent}TypeId::{child}{inner})"
|
||||
return f"crate::codegen::InheritTypes::TopTypeId {{ {protochain[0].lower()}: {inner} }}"
|
||||
|
||||
|
||||
def DOMClass(descriptor):
|
||||
|
@ -2421,7 +2421,7 @@ pub(crate) fn init_class_ops<D: DomTypes>() {{
|
|||
}});
|
||||
}}
|
||||
|
||||
pub(crate) static Class: ThreadUnsafeOnceLock<DOMJSClass> = ThreadUnsafeOnceLock::new();
|
||||
pub static Class: ThreadUnsafeOnceLock<DOMJSClass> = ThreadUnsafeOnceLock::new();
|
||||
|
||||
pub(crate) fn init_domjs_class<D: DomTypes>() {{
|
||||
init_class_ops::<D>();
|
||||
|
@ -2637,13 +2637,10 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config):
|
|||
"""
|
||||
|
||||
imports = [
|
||||
'crate::dom',
|
||||
'crate::dom::bindings::import::base::*',
|
||||
'crate::dom::bindings::codegen::DomTypes::DomTypes',
|
||||
'crate::dom::bindings::conversions::windowproxy_from_handlevalue',
|
||||
'script_bindings::record::Record',
|
||||
'crate::dom::types::*',
|
||||
'crate::dom::windowproxy::WindowProxy',
|
||||
'crate::import::base::*',
|
||||
'crate::codegen::DomTypes::DomTypes',
|
||||
'crate::conversions::windowproxy_from_handlevalue',
|
||||
'crate::record::Record',
|
||||
'js::typedarray',
|
||||
]
|
||||
|
||||
|
@ -2679,13 +2676,13 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config):
|
|||
|
||||
def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs, config):
|
||||
traits = [
|
||||
"crate::dom::bindings::utils::DomHelpers<Self>",
|
||||
"crate::interfaces::DomHelpers<Self>",
|
||||
"js::rust::Trace",
|
||||
"malloc_size_of::MallocSizeOf",
|
||||
"Sized",
|
||||
]
|
||||
joinedTraits = ' + '.join(traits)
|
||||
elements = [CGGeneric(f"pub(crate) trait DomTypes: {joinedTraits} where Self: 'static {{\n")]
|
||||
elements = [CGGeneric(f"pub trait DomTypes: {joinedTraits} where Self: 'static {{\n")]
|
||||
|
||||
def fixupInterfaceTypeReferences(typename):
|
||||
return typename.replace("D::", "Self::")
|
||||
|
@ -2705,10 +2702,10 @@ def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs,
|
|||
chain = chain[:-1]
|
||||
|
||||
if chain:
|
||||
traits += ["crate::dom::bindings::inheritance::Castable"]
|
||||
traits += ["crate::inheritance::Castable"]
|
||||
|
||||
for parent in chain:
|
||||
traits += [f"crate::dom::bindings::conversions::DerivedFrom<Self::{parent}>"]
|
||||
traits += [f"crate::conversions::DerivedFrom<Self::{parent}>"]
|
||||
|
||||
iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
|
||||
if iterableDecl:
|
||||
|
@ -2719,27 +2716,27 @@ def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs,
|
|||
valuetype = fixupInterfaceTypeReferences(
|
||||
getRetvalDeclarationForType(iterableDecl.valueType, descriptor).define()
|
||||
)
|
||||
traits += [f"crate::dom::bindings::like::Maplike<Key={keytype}, Value={valuetype}>"]
|
||||
traits += [f"crate::like::Maplike<Key={keytype}, Value={valuetype}>"]
|
||||
if iterableDecl.isSetlike():
|
||||
keytype = fixupInterfaceTypeReferences(
|
||||
getRetvalDeclarationForType(iterableDecl.keyType, descriptor).define()
|
||||
)
|
||||
traits += [f"crate::dom::bindings::like::Setlike<Key={keytype}>"]
|
||||
traits += [f"crate::like::Setlike<Key={keytype}>"]
|
||||
if iterableDecl.hasKeyType():
|
||||
traits += [
|
||||
"crate::dom::bindings::reflector::DomObjectIteratorWrap<Self>",
|
||||
"crate::dom::bindings::iterable::IteratorDerives",
|
||||
"crate::reflector::DomObjectIteratorWrap<Self>",
|
||||
"crate::iterable::IteratorDerives",
|
||||
]
|
||||
|
||||
if descriptor.weakReferenceable:
|
||||
traits += ["crate::dom::bindings::weakref::WeakReferenceable"]
|
||||
traits += ["crate::weakref::WeakReferenceable"]
|
||||
|
||||
if not descriptor.interface.isNamespace():
|
||||
traits += [
|
||||
"js::conversions::ToJSValConvertible",
|
||||
"crate::dom::bindings::reflector::MutDomObject",
|
||||
"crate::dom::bindings::reflector::DomObject",
|
||||
"crate::dom::bindings::reflector::DomGlobalGeneric<Self>",
|
||||
"crate::reflector::MutDomObject",
|
||||
"crate::reflector::DomObject",
|
||||
"crate::reflector::DomGlobalGeneric<Self>",
|
||||
"malloc_size_of::MallocSizeOf",
|
||||
]
|
||||
|
||||
|
@ -2750,12 +2747,12 @@ def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs,
|
|||
and not descriptor.interface.isIteratorInterface()
|
||||
):
|
||||
traits += [
|
||||
"crate::dom::bindings::conversions::IDLInterface",
|
||||
"crate::conversions::IDLInterface",
|
||||
"PartialEq",
|
||||
]
|
||||
|
||||
if descriptor.concrete and not descriptor.isGlobal():
|
||||
traits += ["crate::dom::bindings::reflector::DomObjectWrap<Self>"]
|
||||
traits += ["crate::reflector::DomObjectWrap<Self>"]
|
||||
|
||||
if not descriptor.interface.isCallback() and not descriptor.interface.isIteratorInterface():
|
||||
nonConstMembers = [m for m in descriptor.interface.members if not m.isConst()]
|
||||
|
@ -2766,7 +2763,7 @@ def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs,
|
|||
or descriptor.interface.legacyFactoryFunctions
|
||||
):
|
||||
namespace = f"{toBindingPath(descriptor)}"
|
||||
traits += [f"crate::dom::bindings::codegen::Bindings::{namespace}::{iface_name}Methods<Self>"]
|
||||
traits += [f"crate::codegen::GenericBindings::{namespace}::{iface_name}Methods<Self>"]
|
||||
isPromise = firstCap(iface_name) == "Promise"
|
||||
elements += [
|
||||
CGGeneric(" #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]\n"),
|
||||
|
@ -2778,8 +2775,8 @@ def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs,
|
|||
]
|
||||
elements += [CGGeneric("}\n")]
|
||||
imports = [
|
||||
CGGeneric("use crate::dom::bindings::root::DomRoot;\n"),
|
||||
CGGeneric("use crate::dom::bindings::str::DOMString;\n"),
|
||||
CGGeneric("use crate::root::DomRoot;\n"),
|
||||
CGGeneric("use crate::str::DOMString;\n"),
|
||||
]
|
||||
return CGList(imports + elements)
|
||||
|
||||
|
@ -3403,7 +3400,7 @@ class CGCollectJSONAttributesMethod(CGAbstractMethod):
|
|||
self.toJSONMethod = toJSONMethod
|
||||
|
||||
def definition_body(self):
|
||||
ret = """let incumbent_global = GlobalScope::incumbent().expect("no incumbent global");
|
||||
ret = """let incumbent_global = D::GlobalScope::incumbent().expect("no incumbent global");
|
||||
let global = incumbent_global.reflector().get_jsobject();\n"""
|
||||
interface = self.descriptor.interface
|
||||
for m in interface.members:
|
||||
|
@ -4322,7 +4319,7 @@ class CGDefaultToJSONMethod(CGSpecializedMethod):
|
|||
|
||||
def definition_body(self):
|
||||
ret = dedent("""
|
||||
use crate::dom::bindings::inheritance::HasParent;
|
||||
use crate::inheritance::HasParent;
|
||||
rooted!(in(cx) let result = JS_NewPlainObject(cx));
|
||||
if result.is_null() {
|
||||
return false;
|
||||
|
@ -4965,7 +4962,7 @@ class CGEnum(CGThing):
|
|||
decl = f"""
|
||||
#[repr(usize)]
|
||||
#[derive({derives})]
|
||||
pub(crate) enum {ident} {{
|
||||
pub enum {ident} {{
|
||||
{enums}
|
||||
}}
|
||||
"""
|
||||
|
@ -4974,10 +4971,10 @@ pub(crate) enum {ident} {{
|
|||
for val in list(enum.values())])
|
||||
|
||||
inner = f"""
|
||||
use crate::dom::bindings::conversions::ConversionResult;
|
||||
use crate::dom::bindings::conversions::FromJSValConvertible;
|
||||
use crate::dom::bindings::conversions::ToJSValConvertible;
|
||||
use crate::dom::bindings::utils::find_enum_value;
|
||||
use crate::utils::find_enum_value;
|
||||
use js::conversions::ConversionResult;
|
||||
use js::conversions::FromJSValConvertible;
|
||||
use js::conversions::ToJSValConvertible;
|
||||
use js::jsapi::JSContext;
|
||||
use js::rust::HandleValue;
|
||||
use js::rust::MutableHandleValue;
|
||||
|
@ -4988,7 +4985,7 @@ pub(crate) const pairs: &[(&str, super::{ident})] = &[
|
|||
];
|
||||
|
||||
impl super::{ident} {{
|
||||
pub(crate) fn as_str(&self) -> &'static str {{
|
||||
pub fn as_str(&self) -> &'static str {{
|
||||
pairs[*self as usize].0
|
||||
}}
|
||||
}}
|
||||
|
@ -5077,7 +5074,7 @@ class CGConstant(CGThing):
|
|||
elif tag == IDLType.Tags.double:
|
||||
const_type = "f64"
|
||||
|
||||
return f"pub(crate) const {name}: {const_type} = {value};\n"
|
||||
return f"pub const {name}: {const_type} = {value};\n"
|
||||
|
||||
|
||||
def getUnionTypeTemplateVars(type, descriptorProvider):
|
||||
|
@ -5208,7 +5205,7 @@ impl{self.generic} Clone for {self.type}{self.genericSuffix} {{
|
|||
manualImpls = "\n".join(map(lambda t: self.manualImpl(t, templateVars), self.manualImpls))
|
||||
return f"""
|
||||
#[derive({", ".join(derives)})]
|
||||
pub(crate) enum {self.type}{self.generic} {{
|
||||
pub enum {self.type}{self.generic} {{
|
||||
{joinedEnumValues}
|
||||
}}
|
||||
|
||||
|
@ -5589,7 +5586,7 @@ class ClassConstructor(ClassItem):
|
|||
|
||||
name = cgClass.getNameString().replace(': DomTypes', '')
|
||||
return f"""
|
||||
pub(crate) unsafe fn {self.getDecorators(True)}new({args}) -> Rc<{name}>{body}
|
||||
pub unsafe fn {self.getDecorators(True)}new({args}) -> Rc<{name}>{body}
|
||||
"""
|
||||
|
||||
def define(self, cgClass):
|
||||
|
@ -5681,7 +5678,7 @@ class CGClass(CGThing):
|
|||
myself = ''
|
||||
if self.decorators != '':
|
||||
myself += f'{self.decorators}\n'
|
||||
myself += f'{self.indent}pub(crate) struct {self.name}{specialization}'
|
||||
myself += f'{self.indent}pub struct {self.name}{specialization}'
|
||||
result += myself
|
||||
|
||||
assert len(self.bases) == 1 # XXjdm Can we support multiple inheritance?
|
||||
|
@ -5689,7 +5686,7 @@ class CGClass(CGThing):
|
|||
result += ' {\n'
|
||||
|
||||
if self.bases:
|
||||
self.members = [ClassMember("parent", self.bases[0].name, "pub(crate)")] + self.members
|
||||
self.members = [ClassMember("parent", self.bases[0].name, "pub")] + self.members
|
||||
|
||||
result += CGIndenter(CGGeneric(self.extradeclarations),
|
||||
len(self.indent)).define()
|
||||
|
@ -6590,7 +6587,7 @@ class CGDOMJSProxyHandlerDOMClass(CGThing):
|
|||
|
||||
def define(self):
|
||||
return f"""
|
||||
pub(crate) static Class: ThreadUnsafeOnceLock<DOMClass> = ThreadUnsafeOnceLock::new();
|
||||
pub static Class: ThreadUnsafeOnceLock<DOMClass> = ThreadUnsafeOnceLock::new();
|
||||
|
||||
pub(crate) fn init_proxy_handler_dom_class<D: DomTypes>() {{
|
||||
Class.set({DOMClass(self.descriptor)});
|
||||
|
@ -6758,7 +6755,7 @@ class CGInterfaceTrait(CGThing):
|
|||
|
||||
name = descriptor.interface.identifier.name
|
||||
self.cgRoot = CGWrapper(CGIndenter(CGList(methods, "")),
|
||||
pre=f"pub(crate) trait {name}Methods<D: DomTypes> {{\n",
|
||||
pre=f"pub trait {name}Methods<D: DomTypes> {{\n",
|
||||
post="}")
|
||||
self.empty = not methods
|
||||
|
||||
|
@ -6793,7 +6790,7 @@ class CGInitStatics(CGThing):
|
|||
] for name in nonempty]
|
||||
flat_specs = [x for xs in specs for x in xs]
|
||||
specs = '\n'.join(flat_specs)
|
||||
module = f"crate::dom::bindings::codegen::Bindings::{toBindingPath(descriptor)}"
|
||||
module = f"crate::codegen::GenericBindings::{toBindingPath(descriptor)}"
|
||||
relevantMethods = [
|
||||
m for m in descriptor.interface.members if m.isMethod()
|
||||
] if not descriptor.interface.isCallback() else []
|
||||
|
@ -7055,8 +7052,7 @@ class CGDescriptor(CGThing):
|
|||
# These are inside the generated module
|
||||
cgThings = CGImports(cgThings, descriptors=[descriptor], callbacks=[],
|
||||
dictionaries=[], enums=[], typedefs=[], imports=[
|
||||
'crate::dom',
|
||||
'crate::dom::bindings::import::module::*',
|
||||
'crate::import::module::*',
|
||||
], config=config)
|
||||
|
||||
cgThings = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
|
||||
|
@ -7066,7 +7062,7 @@ class CGDescriptor(CGThing):
|
|||
if reexports:
|
||||
reexports = ', '.join([reexportedName(name) for name in reexports])
|
||||
namespace = toBindingNamespace(descriptor.name)
|
||||
cgThings = CGList([CGGeneric(f'pub(crate) use self::{namespace}::{{{reexports}}};'),
|
||||
cgThings = CGList([CGGeneric(f'pub use self::{namespace}::{{{reexports}}};'),
|
||||
cgThings], '\n')
|
||||
|
||||
self.cgRoot = cgThings
|
||||
|
@ -7171,10 +7167,10 @@ impl{self.generic} Clone for {self.makeClassName(self.dictionary)}{self.genericS
|
|||
typeName += parentSuffix
|
||||
if type_needs_tracing(d.parent):
|
||||
typeName = f"RootedTraceableBox<{typeName}>"
|
||||
inheritance = f" pub(crate) parent: {typeName},\n"
|
||||
inheritance = f" pub parent: {typeName},\n"
|
||||
else:
|
||||
inheritance = ""
|
||||
memberDecls = [f" pub(crate) {self.makeMemberName(m[0].identifier.name)}: {self.getMemberType(m)},"
|
||||
memberDecls = [f" pub {self.makeMemberName(m[0].identifier.name)}: {self.getMemberType(m)},"
|
||||
for m in self.memberInfo]
|
||||
|
||||
derive = ["JSTraceable"] + self.derives
|
||||
|
@ -7216,7 +7212,7 @@ impl{self.generic} Clone for {self.makeClassName(self.dictionary)}{self.genericS
|
|||
return (
|
||||
f"#[derive({', '.join(derive)})]\n"
|
||||
f"{mustRoot}"
|
||||
f"pub(crate) struct {self.makeClassName(d)}{self.generic} {{\n"
|
||||
f"pub struct {self.makeClassName(d)}{self.generic} {{\n"
|
||||
f"{inheritance}"
|
||||
f"{joinedMemberDecls}\n"
|
||||
"}\n"
|
||||
|
@ -7290,7 +7286,7 @@ impl{self.generic} Clone for {self.makeClassName(self.dictionary)}{self.genericS
|
|||
return (
|
||||
f"impl{self.generic} {selfName}{self.genericSuffix} {{\n"
|
||||
f"{CGIndenter(CGGeneric(self.makeEmpty()), indentLevel=4).define()}\n"
|
||||
" pub(crate) fn new(cx: SafeJSContext, val: HandleValue) \n"
|
||||
" pub fn new(cx: SafeJSContext, val: HandleValue) \n"
|
||||
f" -> Result<ConversionResult<{actualType}>, ()> {{\n"
|
||||
f" {unsafe_if_necessary} {{\n"
|
||||
" let object = if val.get().is_null_or_undefined() {\n"
|
||||
|
@ -7320,7 +7316,7 @@ impl{self.generic} Clone for {self.makeClassName(self.dictionary)}{self.genericS
|
|||
"\n"
|
||||
f"impl{self.generic} {selfName}{self.genericSuffix} {{\n"
|
||||
" #[allow(clippy::wrong_self_convention)]\n"
|
||||
" pub(crate) unsafe fn to_jsobject(&self, cx: *mut JSContext, mut obj: MutableHandleObject) {\n"
|
||||
" pub unsafe fn to_jsobject(&self, cx: *mut JSContext, mut obj: MutableHandleObject) {\n"
|
||||
f"{CGIndenter(CGList(memberInserts), indentLevel=8).define()} }}\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
|
@ -7396,7 +7392,7 @@ impl{self.generic} Clone for {self.makeClassName(self.dictionary)}{self.genericS
|
|||
parentTemplate = "parent: %s::%s::empty(),\n"
|
||||
fieldTemplate = "%s: %s,\n"
|
||||
functionTemplate = (
|
||||
"pub(crate) fn empty() -> Self {\n"
|
||||
"pub fn empty() -> Self {\n"
|
||||
" Self {\n"
|
||||
"%s"
|
||||
" }\n"
|
||||
|
@ -7406,7 +7402,7 @@ impl{self.generic} Clone for {self.makeClassName(self.dictionary)}{self.genericS
|
|||
parentTemplate = "dictionary.parent = %s::%s::empty();\n"
|
||||
fieldTemplate = "dictionary.%s = %s;\n"
|
||||
functionTemplate = (
|
||||
"pub(crate) fn empty() -> RootedTraceableBox<Self> {\n"
|
||||
"pub fn empty() -> RootedTraceableBox<Self> {\n"
|
||||
" let mut dictionary = RootedTraceableBox::new(Self::default());\n"
|
||||
"%s"
|
||||
" dictionary\n"
|
||||
|
@ -7463,7 +7459,7 @@ class CGInitAllStatics(CGAbstractMethod):
|
|||
|
||||
def definition_body(self):
|
||||
return CGList([
|
||||
CGGeneric(f" Bindings::{toBindingModuleFileFromDescriptor(desc)}::{toBindingNamespace(desc.name)}"
|
||||
CGGeneric(f" GenericBindings::{toBindingModuleFileFromDescriptor(desc)}::{toBindingNamespace(desc.name)}"
|
||||
"::init_statics::<D>();")
|
||||
for desc in self.descriptors
|
||||
], "\n")
|
||||
|
@ -7473,18 +7469,21 @@ class CGRegisterProxyHandlersMethod(CGAbstractMethod):
|
|||
def __init__(self, descriptors):
|
||||
docs = "Create the global vtables used by the generated DOM bindings to implement JS proxies."
|
||||
CGAbstractMethod.__init__(self, None, 'RegisterProxyHandlers', 'void', [],
|
||||
unsafe=True, pub=True, docs=docs, templateArgs=["D: DomTypes"])
|
||||
pub=True, docs=docs, templateArgs=["D: DomTypes"])
|
||||
self.descriptors = descriptors
|
||||
|
||||
def definition_body(self):
|
||||
return CGList([
|
||||
body = [CGGeneric("unsafe {")]
|
||||
body += [
|
||||
CGGeneric(f"proxy_handlers::{desc.name}.store(\n"
|
||||
f" Bindings::{toBindingModuleFile(desc.name)}::{toBindingNamespace(desc.name)}"
|
||||
f" GenericBindings::{toBindingModuleFile(desc.name)}::{toBindingNamespace(desc.name)}"
|
||||
"::DefineProxyHandler::<D>() as *mut _,\n"
|
||||
" std::sync::atomic::Ordering::Release,\n"
|
||||
");")
|
||||
for desc in self.descriptors
|
||||
], "\n")
|
||||
]
|
||||
body += [CGGeneric("}")]
|
||||
return CGList(body, "\n")
|
||||
|
||||
|
||||
class CGRegisterProxyHandlers(CGThing):
|
||||
|
@ -7687,11 +7686,11 @@ class CGBindingRoot(CGThing):
|
|||
|
||||
if t.innerType.isUnion() and not t.innerType.nullable():
|
||||
# Allow using the typedef's name for accessing variants.
|
||||
typeDefinition = f"pub(crate) use self::{type.replace('<D>', '')} as {name};"
|
||||
typeDefinition = f"pub use self::{type.replace('<D>', '')} as {name};"
|
||||
else:
|
||||
generic = "<D>" if containsDomInterface(t.innerType) else ""
|
||||
replacedType = type.replace("D::", "<D as DomTypes>::")
|
||||
typeDefinition = f"pub(crate) type {name}{generic} = {replacedType};"
|
||||
typeDefinition = f"pub type {name}{generic} = {replacedType};"
|
||||
|
||||
cgthings.append(CGGeneric(typeDefinition))
|
||||
|
||||
|
@ -7719,7 +7718,7 @@ class CGBindingRoot(CGThing):
|
|||
# These are the global imports (outside of the generated module)
|
||||
curr = CGImports(curr, descriptors=callbackDescriptors, callbacks=mainCallbacks,
|
||||
dictionaries=dictionaries, enums=enums, typedefs=typedefs,
|
||||
imports=['crate::dom::bindings::import::base::*'], config=config)
|
||||
imports=['crate::import::base::*'], config=config)
|
||||
|
||||
# Add the auto-generated comment.
|
||||
curr = CGWrapper(curr, pre=f"{AUTOGENERATED_WARNING_COMMENT}{ALLOWED_WARNINGS}")
|
||||
|
@ -8589,7 +8588,7 @@ class GlobalGenRoots():
|
|||
], "\n")
|
||||
|
||||
return CGImports(code, descriptors=[], callbacks=[], dictionaries=[], enums=[], typedefs=[], imports=[
|
||||
'crate::dom::bindings::codegen::Bindings',
|
||||
'crate::codegen::GenericBindings',
|
||||
'crate::DomTypes',
|
||||
], config=config)
|
||||
|
||||
|
@ -8616,7 +8615,7 @@ class GlobalGenRoots():
|
|||
| set(leafModule(d) for d in config.getDictionaries()))
|
||||
curr = CGList([CGGeneric(
|
||||
"#[allow(clippy::derivable_impls)]\n"
|
||||
f"pub(crate) mod {name};\n"
|
||||
f"pub mod {name};\n"
|
||||
) for name in sorted(descriptors)])
|
||||
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
|
||||
return curr
|
||||
|
@ -8663,6 +8662,7 @@ class GlobalGenRoots():
|
|||
if base in topTypes:
|
||||
typeIdCode.append(CGGeneric(f"""
|
||||
impl {base} {{
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn type_id(&self) -> &'static {base}TypeId {{
|
||||
unsafe {{
|
||||
&get_dom_class(self.reflector().get_jsobject().get())
|
||||
|
@ -8752,7 +8752,7 @@ impl Clone for TopTypeId {
|
|||
unions.add(name)
|
||||
generic = "<crate::DomTypeHolder>" if containsDomInterface(t) else ""
|
||||
cgthings += [CGGeneric(f"pub(crate) type {name} = "
|
||||
f"crate::dom::bindings::codegen::GenericUnionTypes::{name}{generic};\n")]
|
||||
f"script_bindings::codegen::GenericUnionTypes::{name}{generic};\n")]
|
||||
curr = CGList(cgthings)
|
||||
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
|
||||
return curr
|
||||
|
|
|
@ -228,7 +228,7 @@ class Descriptor(DescriptorProvider):
|
|||
self.nativeType = typeName
|
||||
pathDefault = 'crate::dom::types::%s' % typeName
|
||||
elif self.interface.isCallback():
|
||||
ty = 'crate::dom::bindings::codegen::GenericBindings::%sBinding::%s' % (ifaceName, ifaceName)
|
||||
ty = 'crate::codegen::GenericBindings::%sBinding::%s' % (ifaceName, ifaceName)
|
||||
pathDefault = ty
|
||||
self.returnType = "Rc<%s<D>>" % ty
|
||||
self.argumentType = "???"
|
||||
|
@ -238,7 +238,7 @@ class Descriptor(DescriptorProvider):
|
|||
self.argumentType = "&%s%s" % (prefix, typeName)
|
||||
self.nativeType = "*const %s%s" % (prefix, typeName)
|
||||
if self.interface.isIteratorInterface():
|
||||
pathDefault = 'crate::dom::bindings::iterable::IterableIterator'
|
||||
pathDefault = 'crate::iterable::IterableIterator'
|
||||
else:
|
||||
pathDefault = 'crate::dom::types::%s' % MakeNativeName(typeName)
|
||||
|
||||
|
@ -491,7 +491,7 @@ def getIdlFileName(object):
|
|||
|
||||
|
||||
def getModuleFromObject(object):
|
||||
return ('crate::dom::bindings::codegen::GenericBindings::' + getIdlFileName(object) + 'Binding')
|
||||
return ('crate::codegen::GenericBindings::' + getIdlFileName(object) + 'Binding')
|
||||
|
||||
|
||||
def getTypesFromDescriptor(descriptor):
|
||||
|
@ -552,5 +552,5 @@ def iteratorNativeType(descriptor, infer=False):
|
|||
res = "IterableIterator%s" % ("" if infer else '<D, D::%s>' % descriptor.interface.identifier.name)
|
||||
# todo: this hack is telling us that something is still wrong in codegen
|
||||
if iterableDecl.isSetlike() or iterableDecl.isMaplike():
|
||||
res = f"crate::dom::bindings::iterable::{res}"
|
||||
res = f"crate::iterable::{res}"
|
||||
return res
|
||||
|
|
37
components/script_bindings/constructor.rs
Normal file
37
components/script_bindings/constructor.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* 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::ptr;
|
||||
|
||||
use js::jsapi::{CallArgs, JSObject};
|
||||
use js::rust::HandleObject;
|
||||
|
||||
use crate::codegen::PrototypeList;
|
||||
use crate::error::throw_constructor_without_new;
|
||||
use crate::interface::get_desired_proto;
|
||||
use crate::script_runtime::JSContext;
|
||||
use crate::utils::ProtoOrIfaceArray;
|
||||
|
||||
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())
|
||||
}
|
|
@ -13,21 +13,24 @@ use js::glue::{
|
|||
JS_GetReservedSlot, UnwrapObjectDynamic,
|
||||
};
|
||||
use js::jsapi::{
|
||||
JS_DeprecatedStringHasLatin1Chars, JS_GetLatin1StringCharsAndLength,
|
||||
Heap, IsWindowProxy, JS_DeprecatedStringHasLatin1Chars, JS_GetLatin1StringCharsAndLength,
|
||||
JS_GetTwoByteStringCharsAndLength, JS_NewStringCopyN, JSContext, JSObject, JSString,
|
||||
};
|
||||
use js::jsval::{ObjectValue, StringValue, UndefinedValue};
|
||||
use js::rust::wrappers::IsArrayObject;
|
||||
use js::rust::{
|
||||
HandleId, HandleValue, MutableHandleValue, ToString, get_object_class, is_dom_class,
|
||||
is_dom_object, maybe_wrap_value,
|
||||
};
|
||||
use num_traits::Float;
|
||||
|
||||
use crate::JSTraceable;
|
||||
use crate::inheritance::Castable;
|
||||
use crate::num::Finite;
|
||||
use crate::reflector::{DomObject, Reflector};
|
||||
use crate::root::DomRoot;
|
||||
use crate::str::{ByteString, DOMString, USVString};
|
||||
use crate::trace::RootedTraceableBox;
|
||||
use crate::utils::{DOMClass, DOMJSClass};
|
||||
|
||||
/// A trait to check whether a given `JSObject` implements an IDL interface.
|
||||
|
@ -494,3 +497,88 @@ where
|
|||
}
|
||||
native_from_object(v.get().to_object(), cx)
|
||||
}
|
||||
|
||||
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),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether `value` is an array-like object (Array, FileList,
|
||||
/// HTMLCollection, HTMLFormControlsCollection, HTMLOptionsCollection,
|
||||
/// NodeList).
|
||||
///
|
||||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
pub unsafe fn is_array_like<D: crate::DomTypes>(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::<D::FileList>(object, cx).is_ok() {
|
||||
return true;
|
||||
}
|
||||
if root_from_object::<D::HTMLCollection>(object, cx).is_ok() {
|
||||
return true;
|
||||
}
|
||||
if root_from_object::<D::HTMLFormControlsCollection>(object, cx).is_ok() {
|
||||
return true;
|
||||
}
|
||||
if root_from_object::<D::HTMLOptionsCollection>(object, cx).is_ok() {
|
||||
return true;
|
||||
}
|
||||
if root_from_object::<D::NodeList>(object, cx).is_ok() {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// 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))
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ unsafe fn do_finalize_global(obj: *mut JSObject) {
|
|||
|
||||
/// # Safety
|
||||
/// `this` must point to a valid, non-null instance of T.
|
||||
pub unsafe fn finalize_common<T>(this: *const T) {
|
||||
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);
|
||||
|
@ -41,7 +41,7 @@ pub unsafe fn finalize_common<T>(this: *const T) {
|
|||
/// # Safety
|
||||
/// `obj` must point to a valid, non-null JS object.
|
||||
/// `this` must point to a valid, non-null instance of T.
|
||||
pub unsafe fn finalize_global<T>(obj: *mut JSObject, this: *const T) {
|
||||
pub(crate) unsafe fn finalize_global<T>(obj: *mut JSObject, this: *const T) {
|
||||
do_finalize_global(obj);
|
||||
finalize_common::<T>(this);
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ pub unsafe fn finalize_global<T>(obj: *mut JSObject, this: *const T) {
|
|||
/// # Safety
|
||||
/// `obj` must point to a valid, non-null JS object.
|
||||
/// `this` must point to a valid, non-null instance of T.
|
||||
pub unsafe fn finalize_weak_referenceable<T: WeakReferenceable>(
|
||||
pub(crate) unsafe fn finalize_weak_referenceable<T: WeakReferenceable>(
|
||||
obj: *mut JSObject,
|
||||
this: *const T,
|
||||
) {
|
||||
|
|
96
components/script_bindings/guard.rs
Normal file
96
components/script_bindings/guard.rs
Normal file
|
@ -0,0 +1,96 @@
|
|||
/* 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::DomTypes;
|
||||
use crate::codegen::Globals::Globals;
|
||||
use crate::interface::is_exposed_in;
|
||||
use crate::interfaces::GlobalScopeHelpers;
|
||||
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<D: DomTypes>(
|
||||
&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::<D>(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(Globals),
|
||||
SecureContext(),
|
||||
/// The condition is always satisfied.
|
||||
Satisfied,
|
||||
}
|
||||
|
||||
fn is_secure_context<D: DomTypes>(cx: JSContext) -> bool {
|
||||
unsafe {
|
||||
let in_realm_proof = AlreadyInRealm::assert_for_cx(JSContext::from_ptr(*cx));
|
||||
D::GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)).is_secure_context()
|
||||
}
|
||||
}
|
||||
|
||||
impl Condition {
|
||||
pub(crate) fn is_satisfied<D: DomTypes>(
|
||||
&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::<D>(cx),
|
||||
Condition::Satisfied => true,
|
||||
}
|
||||
}
|
||||
}
|
137
components/script_bindings/import.rs
Normal file
137
components/script_bindings/import.rs
Normal file
|
@ -0,0 +1,137 @@
|
|||
/* 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/. */
|
||||
|
||||
pub(crate) mod base {
|
||||
pub(crate) use std::ptr;
|
||||
pub(crate) use std::rc::Rc;
|
||||
|
||||
pub(crate) use js::conversions::{
|
||||
ConversionBehavior, ConversionResult, FromJSValConvertible, ToJSValConvertible,
|
||||
};
|
||||
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 crate::callback::{
|
||||
CallSetup, CallbackContainer, CallbackFunction, CallbackInterface, CallbackObject,
|
||||
ExceptionHandling, ThisReflector, wrap_call_this_value,
|
||||
};
|
||||
pub(crate) use crate::codegen::DomTypes::DomTypes;
|
||||
pub(crate) use crate::codegen::GenericUnionTypes;
|
||||
pub(crate) use crate::conversions::{StringificationBehavior, root_from_handlevalue};
|
||||
pub(crate) use crate::error::Error::JSFailed;
|
||||
pub(crate) use crate::error::Fallible;
|
||||
pub(crate) use crate::interfaces::*;
|
||||
pub(crate) use crate::lock::ThreadUnsafeOnceLock;
|
||||
pub(crate) use crate::num::Finite;
|
||||
pub(crate) use crate::proxyhandler::CrossOriginProperties;
|
||||
pub(crate) use crate::reflector::{DomGlobalGeneric, DomObject};
|
||||
pub(crate) use crate::root::DomRoot;
|
||||
pub(crate) use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
||||
pub(crate) use crate::str::{ByteString, DOMString, USVString};
|
||||
pub(crate) use crate::trace::RootedTraceableBox;
|
||||
pub(crate) use crate::utils::{get_dictionary_property, set_dictionary_property};
|
||||
}
|
||||
|
||||
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::conversions::ToJSValConvertible;
|
||||
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, typedarray,
|
||||
};
|
||||
pub(crate) use servo_config::pref;
|
||||
|
||||
pub(crate) use super::base::*;
|
||||
pub(crate) use crate::codegen::Globals::Globals;
|
||||
pub(crate) use crate::codegen::{PrototypeList, RegisterBindings};
|
||||
pub(crate) use crate::constant::{ConstantSpec, ConstantVal};
|
||||
pub(crate) use crate::constructor::call_default_constructor;
|
||||
pub(crate) use crate::conversions::{
|
||||
DOM_OBJECT_SLOT, StringificationBehavior, is_array_like, jsid_to_string,
|
||||
native_from_handlevalue, native_from_object_static,
|
||||
};
|
||||
pub(crate) use crate::error::{Error, ErrorResult};
|
||||
pub(crate) use crate::finalize::{
|
||||
finalize_common, finalize_global, finalize_weak_referenceable,
|
||||
};
|
||||
pub(crate) use crate::guard::{Condition, Guard};
|
||||
pub(crate) use crate::inheritance::Castable;
|
||||
pub(crate) use crate::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_per_interface_object_handle, is_exposed_in,
|
||||
};
|
||||
pub(crate) use crate::iterable::{Iterable, IterableIterator, IteratorType};
|
||||
pub(crate) use crate::like::{Maplike, Setlike};
|
||||
pub(crate) use crate::mem::malloc_size_of_including_raw_self;
|
||||
pub(crate) use crate::namespace::{NamespaceObjectClass, create_namespace_object};
|
||||
pub(crate) use crate::proxyhandler::{
|
||||
ensure_expando_object, get_expando_object, set_property_descriptor,
|
||||
};
|
||||
pub(crate) use crate::realms::{AlreadyInRealm, InRealm};
|
||||
pub(crate) use crate::root::{Dom, DomSlice, MaybeUnreflectedDom, Root};
|
||||
pub(crate) use crate::script_runtime::CanGc;
|
||||
pub(crate) use crate::utils::{
|
||||
AsVoidPtr, DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, DOMClass, DOMJSClass, JSCLASS_DOM_GLOBAL,
|
||||
ProtoOrIfaceArray, enumerate_global, exception_to_promise, generic_getter,
|
||||
generic_lenient_getter, generic_lenient_setter, generic_method, generic_setter,
|
||||
generic_static_promise_method, get_array_index_from_id, get_property_on_prototype,
|
||||
has_property_on_prototype, resolve_global, trace_global,
|
||||
};
|
||||
pub(crate) use crate::weakref::DOM_WEAK_SLOT;
|
||||
pub(crate) use crate::{JSTraceable, proxyhandler};
|
||||
}
|
697
components/script_bindings/interface.rs
Normal file
697
components/script_bindings/interface.rs
Normal file
|
@ -0,0 +1,697 @@
|
|||
/* 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 servo_url::MutableOrigin;
|
||||
|
||||
use crate::DomTypes;
|
||||
use crate::codegen::Globals::Globals;
|
||||
use crate::codegen::PrototypeList;
|
||||
use crate::constant::{ConstantSpec, define_constants};
|
||||
use crate::conversions::{DOM_OBJECT_SLOT, get_dom_class};
|
||||
use crate::guard::Guard;
|
||||
use crate::principals::ServoJSPrincipals;
|
||||
use crate::script_runtime::JSContext as SafeJSContext;
|
||||
use crate::utils::{
|
||||
DOM_PROTOTYPE_SLOT, DOMJSClass, JSCLASS_DOM_GLOBAL, ProtoOrIfaceArray, get_proto_or_iface_array,
|
||||
};
|
||||
|
||||
/// 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<D: DomTypes>(
|
||||
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::<D>(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<D: DomTypes>(
|
||||
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::<D>(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<D: DomTypes>(
|
||||
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::<D>(
|
||||
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<D: DomTypes>(
|
||||
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::<D>(
|
||||
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<D: DomTypes>(
|
||||
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::<D>(cx, rval.handle(), methods, global);
|
||||
define_guarded_properties::<D>(cx, rval.handle(), properties, global);
|
||||
define_guarded_constants::<D>(cx, rval.handle(), constants, global);
|
||||
}
|
||||
|
||||
/// Conditionally define constants on an object.
|
||||
pub(crate) fn define_guarded_constants<D: DomTypes>(
|
||||
cx: SafeJSContext,
|
||||
obj: HandleObject,
|
||||
constants: &[Guard<&[ConstantSpec]>],
|
||||
global: HandleObject,
|
||||
) {
|
||||
for guard in constants {
|
||||
if let Some(specs) = guard.expose::<D>(cx, obj, global) {
|
||||
define_constants(cx, obj, specs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Conditionally define methods on an object.
|
||||
pub(crate) fn define_guarded_methods<D: DomTypes>(
|
||||
cx: SafeJSContext,
|
||||
obj: HandleObject,
|
||||
methods: &[Guard<&'static [JSFunctionSpec]>],
|
||||
global: HandleObject,
|
||||
) {
|
||||
for guard in methods {
|
||||
if let Some(specs) = guard.expose::<D>(cx, obj, global) {
|
||||
unsafe {
|
||||
define_methods(*cx, obj, specs).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Conditionally define properties on an object.
|
||||
pub(crate) fn define_guarded_properties<D: DomTypes>(
|
||||
cx: SafeJSContext,
|
||||
obj: HandleObject,
|
||||
properties: &[Guard<&'static [JSPropertySpec]>],
|
||||
global: HandleObject,
|
||||
) {
|
||||
for guard in properties {
|
||||
if let Some(specs) = guard.expose::<D>(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)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub 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.set(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(())
|
||||
}
|
||||
}
|
|
@ -2,14 +2,103 @@
|
|||
* 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 js::rust::{HandleObject, MutableHandleObject};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::thread::LocalKey;
|
||||
|
||||
use crate::script_runtime::JSContext;
|
||||
use js::conversions::ToJSValConvertible;
|
||||
use js::glue::JSPrincipalsCallbacks;
|
||||
use js::jsapi::{CallArgs, HandleObject as RawHandleObject, JSContext as RawJSContext, JSObject};
|
||||
use js::rust::{HandleObject, MutableHandleObject};
|
||||
use servo_url::{MutableOrigin, ServoUrl};
|
||||
|
||||
use crate::DomTypes;
|
||||
use crate::codegen::PrototypeList;
|
||||
use crate::conversions::DerivedFrom;
|
||||
use crate::error::Error;
|
||||
use crate::realms::InRealm;
|
||||
use crate::reflector::{DomObject, DomObjectWrap};
|
||||
use crate::root::DomRoot;
|
||||
use crate::script_runtime::{CanGc, JSContext};
|
||||
use crate::settings_stack::StackEntry;
|
||||
use crate::utils::ProtoOrIfaceArray;
|
||||
|
||||
/// Operations that must be invoked from the generated bindings.
|
||||
pub trait DomHelpers<D: DomTypes> {
|
||||
fn throw_dom_exception(cx: JSContext, global: &D::GlobalScope, result: Error, can_gc: CanGc);
|
||||
|
||||
fn call_html_constructor<T: DerivedFrom<D::Element> + DomObject>(
|
||||
cx: JSContext,
|
||||
args: &CallArgs,
|
||||
global: &D::GlobalScope,
|
||||
proto_id: PrototypeList::ID,
|
||||
creator: unsafe fn(JSContext, HandleObject, *mut ProtoOrIfaceArray),
|
||||
can_gc: CanGc,
|
||||
) -> bool;
|
||||
|
||||
fn settings_stack() -> &'static LocalKey<RefCell<Vec<StackEntry<D>>>>;
|
||||
|
||||
fn principals_callbacks() -> &'static JSPrincipalsCallbacks;
|
||||
|
||||
fn is_platform_object_same_origin(cx: JSContext, obj: RawHandleObject) -> bool;
|
||||
|
||||
fn interface_map() -> &'static phf::Map<&'static [u8], for<'a> fn(JSContext, HandleObject)>;
|
||||
|
||||
fn push_new_element_queue();
|
||||
fn pop_current_element_queue(can_gc: CanGc);
|
||||
|
||||
fn reflect_dom_object<T, U>(obj: Box<T>, global: &U, can_gc: CanGc) -> DomRoot<T>
|
||||
where
|
||||
T: DomObject + DomObjectWrap<D>,
|
||||
U: DerivedFrom<D::GlobalScope>;
|
||||
|
||||
fn report_pending_exception(cx: JSContext, dispatch_event: bool, realm: InRealm, can_gc: CanGc);
|
||||
}
|
||||
|
||||
/// Operations that must be invoked from the generated bindings.
|
||||
#[allow(unsafe_code)]
|
||||
pub trait GlobalScopeHelpers<D: DomTypes> {
|
||||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null RawJSContext.
|
||||
unsafe fn from_context(cx: *mut RawJSContext, realm: InRealm) -> DomRoot<D::GlobalScope>;
|
||||
fn get_cx() -> JSContext;
|
||||
/// # Safety
|
||||
/// `obj` must point to a valid, non-null JSObject.
|
||||
unsafe fn from_object(obj: *mut JSObject) -> DomRoot<D::GlobalScope>;
|
||||
fn from_reflector(reflector: &impl DomObject, realm: InRealm) -> DomRoot<D::GlobalScope>;
|
||||
|
||||
/// # Safety
|
||||
/// `obj` must point to a valid, non-null JSObject.
|
||||
/// `cx` must point to a valid, non-null RawJSContext.
|
||||
unsafe fn from_object_maybe_wrapped(
|
||||
obj: *mut JSObject,
|
||||
cx: *mut RawJSContext,
|
||||
) -> DomRoot<D::GlobalScope>;
|
||||
|
||||
fn origin(&self) -> &MutableOrigin;
|
||||
|
||||
fn incumbent() -> Option<DomRoot<D::GlobalScope>>;
|
||||
|
||||
fn perform_a_microtask_checkpoint(&self, can_gc: CanGc);
|
||||
|
||||
fn get_url(&self) -> ServoUrl;
|
||||
|
||||
fn is_secure_context(&self) -> bool;
|
||||
}
|
||||
|
||||
pub trait DocumentHelpers {
|
||||
fn ensure_safe_to_run_script_or_layout(&self);
|
||||
}
|
||||
|
||||
/// Operations that must be invoked from the generated bindings.
|
||||
pub trait PromiseHelpers<D: crate::DomTypes> {
|
||||
fn new_resolved(
|
||||
global: &D::GlobalScope,
|
||||
cx: JSContext,
|
||||
value: impl ToJSValConvertible,
|
||||
) -> Rc<D::Promise>;
|
||||
}
|
||||
|
||||
pub trait ServoInternalsHelpers {
|
||||
fn is_servo_internal(cx: JSContext, global: HandleObject) -> bool;
|
||||
}
|
||||
|
|
|
@ -2,13 +2,36 @@
|
|||
* 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 js::conversions::ToJSValConvertible;
|
||||
//! 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 crate::codegen::GenericBindings::IterableIteratorBinding::{
|
||||
IterableKeyAndValueResult, IterableKeyOrValueResult,
|
||||
};
|
||||
use crate::conversions::IDLInterface;
|
||||
use crate::error::Fallible;
|
||||
use crate::interfaces::DomHelpers;
|
||||
use crate::realms::InRealm;
|
||||
use crate::reflector::{DomGlobalGeneric, DomObjectIteratorWrap, DomObjectWrap, Reflector};
|
||||
use crate::root::{Dom, DomRoot, Root};
|
||||
use crate::script_runtime::{CanGc, JSContext};
|
||||
use crate::trace::{NoTrace, RootedTraceableBox};
|
||||
use crate::utils::DOMClass;
|
||||
use crate::{DomTypes, JSTraceable};
|
||||
|
||||
/// The values that an iterator will iterate over.
|
||||
#[derive(JSTraceable, MallocSizeOf)]
|
||||
pub enum IteratorType {
|
||||
pub(crate) enum IteratorType {
|
||||
/// The keys of the iterable object.
|
||||
Keys,
|
||||
/// The values of the iterable object.
|
||||
|
@ -38,3 +61,148 @@ pub trait Iterable {
|
|||
pub trait IteratorDerives {
|
||||
fn derives(class: &'static DOMClass) -> bool;
|
||||
}
|
||||
|
||||
/// An iterator over the iterable entries of a given DOM interface.
|
||||
#[dom_struct]
|
||||
pub 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),
|
||||
});
|
||||
<D as DomHelpers<D>>::reflect_dom_object(iterator, &*iterable.global_(realm), CanGc::note())
|
||||
}
|
||||
|
||||
/// Return the next value from the iterable object.
|
||||
#[allow(non_snake_case)]
|
||||
pub 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(())
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#![cfg_attr(any(doc, clippy), allow(unknown_lints))]
|
||||
#![deny(crown_is_not_used)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate js;
|
||||
#[macro_use]
|
||||
extern crate jstraceable_derive;
|
||||
#[macro_use]
|
||||
|
@ -18,21 +20,30 @@ extern crate log;
|
|||
extern crate malloc_size_of_derive;
|
||||
|
||||
pub mod callback;
|
||||
pub mod constant;
|
||||
mod constant;
|
||||
mod constructor;
|
||||
pub mod conversions;
|
||||
pub mod error;
|
||||
pub mod finalize;
|
||||
mod finalize;
|
||||
mod guard;
|
||||
mod import;
|
||||
pub mod inheritance;
|
||||
pub mod interface;
|
||||
pub mod interfaces;
|
||||
pub mod iterable;
|
||||
pub mod like;
|
||||
pub mod lock;
|
||||
mod lock;
|
||||
mod mem;
|
||||
mod namespace;
|
||||
pub mod num;
|
||||
pub mod principals;
|
||||
pub mod proxyhandler;
|
||||
pub mod realms;
|
||||
pub mod record;
|
||||
pub mod reflector;
|
||||
pub mod root;
|
||||
pub mod script_runtime;
|
||||
pub mod settings_stack;
|
||||
pub mod str;
|
||||
pub mod trace;
|
||||
pub mod utils;
|
||||
|
@ -51,6 +62,32 @@ pub mod codegen {
|
|||
pub mod PrototypeList {
|
||||
include!(concat!(env!("OUT_DIR"), "/PrototypeList.rs"));
|
||||
}
|
||||
pub(crate) mod DomTypes {
|
||||
include!(concat!(env!("OUT_DIR"), "/DomTypes.rs"));
|
||||
}
|
||||
#[allow(
|
||||
dead_code,
|
||||
clippy::extra_unused_type_parameters,
|
||||
clippy::missing_safety_doc,
|
||||
clippy::result_unit_err
|
||||
)]
|
||||
pub mod GenericBindings {
|
||||
include!(concat!(env!("OUT_DIR"), "/Bindings/mod.rs"));
|
||||
}
|
||||
#[allow(
|
||||
non_camel_case_types,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
clippy::large_enum_variant,
|
||||
clippy::upper_case_acronyms,
|
||||
clippy::enum_variant_names
|
||||
)]
|
||||
pub mod GenericUnionTypes {
|
||||
include!(concat!(env!("OUT_DIR"), "/GenericUnionTypes.rs"));
|
||||
}
|
||||
pub mod RegisterBindings {
|
||||
include!(concat!(env!("OUT_DIR"), "/RegisterBindings.rs"));
|
||||
}
|
||||
}
|
||||
|
||||
// These trait exports are public, because they are used in the DOM bindings.
|
||||
|
@ -58,4 +95,6 @@ pub mod codegen {
|
|||
// it is useful that they are accessible at the root of the crate.
|
||||
pub(crate) use js::gc::Traceable as JSTraceable;
|
||||
|
||||
pub use crate::codegen::DomTypes::DomTypes;
|
||||
pub(crate) use crate::reflector::{DomObject, MutDomObject, Reflector};
|
||||
pub(crate) use crate::trace::CustomTraceable;
|
||||
|
|
20
components/script_bindings/mem.rs
Normal file
20
components/script_bindings/mem.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
/* 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/. */
|
||||
|
||||
//! Routines for handling measuring the memory usage of arbitrary DOM nodes.
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
|
||||
/// Used by codegen to include the pointer to the `MallocSizeOf` implementation of each
|
||||
/// IDL interface. This way we don't have to find the most-derived interface of DOM
|
||||
/// objects by hand in code.
|
||||
#[allow(unsafe_code)]
|
||||
pub(crate) unsafe fn malloc_size_of_including_raw_self<T: MallocSizeOf>(
|
||||
ops: &mut MallocSizeOfOps,
|
||||
obj: *const c_void,
|
||||
) -> usize {
|
||||
ops.malloc_size_of(obj) + (*(obj as *const T)).size_of(ops)
|
||||
}
|
62
components/script_bindings/namespace.rs
Normal file
62
components/script_bindings/namespace.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
/* 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 crate::DomTypes;
|
||||
use crate::constant::ConstantSpec;
|
||||
use crate::guard::Guard;
|
||||
use crate::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<D: DomTypes>(
|
||||
cx: JSContext,
|
||||
global: HandleObject,
|
||||
proto: HandleObject,
|
||||
class: &'static NamespaceObjectClass,
|
||||
methods: &[Guard<&'static [JSFunctionSpec]>],
|
||||
constants: &[Guard<&'static [ConstantSpec]>],
|
||||
name: &CStr,
|
||||
mut rval: MutableHandleObject,
|
||||
) {
|
||||
create_object::<D>(
|
||||
cx,
|
||||
global,
|
||||
proto,
|
||||
&class.0,
|
||||
methods,
|
||||
&[],
|
||||
constants,
|
||||
rval.reborrow(),
|
||||
);
|
||||
define_on_global_object(cx, global, name, rval.handle());
|
||||
}
|
128
components/script_bindings/principals.rs
Normal file
128
components/script_bindings/principals.rs
Normal file
|
@ -0,0 +1,128 @@
|
|||
/* 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::marker::PhantomData;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::Deref;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use js::glue::{CreateRustJSPrincipals, GetRustJSPrincipalsPrivate};
|
||||
use js::jsapi::{JS_DropPrincipals, JS_HoldPrincipals, JSPrincipals};
|
||||
use js::rust::Runtime;
|
||||
use servo_url::MutableOrigin;
|
||||
|
||||
use crate::DomTypes;
|
||||
use crate::interfaces::DomHelpers;
|
||||
|
||||
/// An owned reference to Servo's `JSPrincipals` instance.
|
||||
#[repr(transparent)]
|
||||
pub struct ServoJSPrincipals(NonNull<JSPrincipals>);
|
||||
|
||||
impl ServoJSPrincipals {
|
||||
pub fn new<D: DomTypes>(origin: &MutableOrigin) -> Self {
|
||||
unsafe {
|
||||
let private: Box<MutableOrigin> = Box::new(origin.clone());
|
||||
let raw = CreateRustJSPrincipals(
|
||||
<D as DomHelpers<D>>::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.
|
||||
///
|
||||
/// # Safety
|
||||
/// `raw` must point to a valid JSPrincipals value.
|
||||
#[inline]
|
||||
pub unsafe fn from_raw_nonnull(raw: NonNull<JSPrincipals>) -> Self {
|
||||
JS_HoldPrincipals(raw.as_ptr());
|
||||
Self(raw)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn origin(&self) -> MutableOrigin {
|
||||
unsafe {
|
||||
let origin = GetRustJSPrincipalsPrivate(self.0.as_ptr()) as *mut MutableOrigin;
|
||||
(*origin).clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_raw_nonnull(&self) -> NonNull<JSPrincipals> {
|
||||
self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub 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 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 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 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
|
||||
}
|
||||
}
|
|
@ -8,15 +8,20 @@ use std::ffi::CStr;
|
|||
use std::os::raw::c_char;
|
||||
use std::ptr;
|
||||
|
||||
use js::glue::{GetProxyHandlerFamily, GetProxyPrivate, SetProxyPrivate};
|
||||
use js::conversions::ToJSValConvertible;
|
||||
use js::glue::{
|
||||
GetProxyHandler, GetProxyHandlerFamily, GetProxyPrivate, InvokeGetOwnPropertyDescriptor,
|
||||
SetProxyPrivate,
|
||||
};
|
||||
use js::jsapi::{
|
||||
DOMProxyShadowsResult, GetStaticPrototype, GetWellKnownSymbol, Handle as RawHandle,
|
||||
HandleId as RawHandleId, HandleObject as RawHandleObject, JS_AtomizeAndPinString,
|
||||
JS_DefinePropertyById, JS_GetOwnPropertyDescriptorById, JSContext, JSErrNum, JSFunctionSpec,
|
||||
JSObject, JSPropertySpec, MutableHandle as RawMutableHandle,
|
||||
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, ObjectOpResult, PropertyDescriptor,
|
||||
SetDOMProxyInformation, SymbolCode, jsid,
|
||||
MutableHandleObject as RawMutableHandleObject, MutableHandleValue as RawMutableHandleValue,
|
||||
ObjectOpResult, PropertyDescriptor, SetDOMProxyInformation, SymbolCode, jsid,
|
||||
};
|
||||
use js::jsid::SymbolId;
|
||||
use js::jsval::{ObjectValue, UndefinedValue};
|
||||
|
@ -27,8 +32,13 @@ use js::rust::wrappers::{
|
|||
use js::rust::{Handle, HandleObject, HandleValue, MutableHandle, MutableHandleObject};
|
||||
use js::{jsapi, rooted};
|
||||
|
||||
use crate::DomTypes;
|
||||
use crate::conversions::{is_dom_proxy, jsid_to_string, jsstring_to_str};
|
||||
use crate::script_runtime::JSContext as SafeJSContext;
|
||||
use crate::error::Error;
|
||||
use crate::interfaces::{DomHelpers, GlobalScopeHelpers};
|
||||
use crate::realms::{AlreadyInRealm, InRealm};
|
||||
use crate::reflector::DomObject;
|
||||
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
||||
use crate::str::DOMString;
|
||||
use crate::utils::delete_property_by_id;
|
||||
|
||||
|
@ -36,7 +46,7 @@ use crate::utils::delete_property_by_id;
|
|||
///
|
||||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
pub unsafe extern "C" fn shadow_check_callback(
|
||||
pub(crate) unsafe extern "C" fn shadow_check_callback(
|
||||
cx: *mut JSContext,
|
||||
object: RawHandleObject,
|
||||
id: RawHandleId,
|
||||
|
@ -78,7 +88,7 @@ pub fn init() {
|
|||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
/// `result` must point to a valid, non-null ObjectOpResult.
|
||||
pub unsafe extern "C" fn define_property(
|
||||
pub(crate) unsafe extern "C" fn define_property(
|
||||
cx: *mut JSContext,
|
||||
proxy: RawHandleObject,
|
||||
id: RawHandleId,
|
||||
|
@ -95,7 +105,7 @@ pub unsafe extern "C" fn define_property(
|
|||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
/// `bp` must point to a valid, non-null ObjectOpResult.
|
||||
pub unsafe extern "C" fn delete(
|
||||
pub(crate) unsafe extern "C" fn delete(
|
||||
cx: *mut JSContext,
|
||||
proxy: RawHandleObject,
|
||||
id: RawHandleId,
|
||||
|
@ -115,7 +125,7 @@ pub unsafe extern "C" fn delete(
|
|||
///
|
||||
/// # Safety
|
||||
/// `result` must point to a valid, non-null ObjectOpResult.
|
||||
pub unsafe extern "C" fn prevent_extensions(
|
||||
pub(crate) unsafe extern "C" fn prevent_extensions(
|
||||
_cx: *mut JSContext,
|
||||
_proxy: RawHandleObject,
|
||||
result: *mut ObjectOpResult,
|
||||
|
@ -128,7 +138,7 @@ pub unsafe extern "C" fn prevent_extensions(
|
|||
///
|
||||
/// # Safety
|
||||
/// `succeeded` must point to a valid, non-null bool.
|
||||
pub unsafe extern "C" fn is_extensible(
|
||||
pub(crate) unsafe extern "C" fn is_extensible(
|
||||
_cx: *mut JSContext,
|
||||
_proxy: RawHandleObject,
|
||||
succeeded: *mut bool,
|
||||
|
@ -149,7 +159,7 @@ pub unsafe extern "C" fn is_extensible(
|
|||
///
|
||||
/// # Safety
|
||||
/// `is_ordinary` must point to a valid, non-null bool.
|
||||
pub unsafe extern "C" fn get_prototype_if_ordinary(
|
||||
pub(crate) unsafe extern "C" fn get_prototype_if_ordinary(
|
||||
_: *mut JSContext,
|
||||
proxy: RawHandleObject,
|
||||
is_ordinary: *mut bool,
|
||||
|
@ -161,7 +171,7 @@ pub unsafe extern "C" fn get_prototype_if_ordinary(
|
|||
}
|
||||
|
||||
/// Get the expando object, or null if there is none.
|
||||
pub fn get_expando_object(obj: RawHandleObject, mut expando: MutableHandleObject) {
|
||||
pub(crate) fn get_expando_object(obj: RawHandleObject, mut expando: MutableHandleObject) {
|
||||
unsafe {
|
||||
assert!(is_dom_proxy(obj.get()));
|
||||
let val = &mut UndefinedValue();
|
||||
|
@ -179,7 +189,7 @@ pub fn get_expando_object(obj: RawHandleObject, mut expando: MutableHandleObject
|
|||
///
|
||||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
pub unsafe fn ensure_expando_object(
|
||||
pub(crate) unsafe fn ensure_expando_object(
|
||||
cx: *mut JSContext,
|
||||
obj: RawHandleObject,
|
||||
mut expando: MutableHandleObject,
|
||||
|
@ -212,7 +222,7 @@ pub fn set_property_descriptor(
|
|||
*is_none = false;
|
||||
}
|
||||
|
||||
pub fn id_to_source(cx: SafeJSContext, id: RawHandleId) -> Option<DOMString> {
|
||||
pub(crate) fn id_to_source(cx: SafeJSContext, id: RawHandleId) -> Option<DOMString> {
|
||||
unsafe {
|
||||
rooted!(in(*cx) let mut value = UndefinedValue());
|
||||
rooted!(in(*cx) let mut jsstr = ptr::null_mut::<jsapi::JSString>());
|
||||
|
@ -230,9 +240,9 @@ pub fn id_to_source(cx: SafeJSContext, id: RawHandleId) -> Option<DOMString> {
|
|||
/// [`CrossOriginProperties(O)`].
|
||||
///
|
||||
/// [`CrossOriginProperties(O)`]: https://html.spec.whatwg.org/multipage/#crossoriginproperties-(-o-)
|
||||
pub struct CrossOriginProperties {
|
||||
pub attributes: &'static [JSPropertySpec],
|
||||
pub methods: &'static [JSFunctionSpec],
|
||||
pub(crate) struct CrossOriginProperties {
|
||||
pub(crate) attributes: &'static [JSPropertySpec],
|
||||
pub(crate) methods: &'static [JSFunctionSpec],
|
||||
}
|
||||
|
||||
impl CrossOriginProperties {
|
||||
|
@ -250,7 +260,7 @@ impl CrossOriginProperties {
|
|||
/// Implementation of [`CrossOriginOwnPropertyKeys`].
|
||||
///
|
||||
/// [`CrossOriginOwnPropertyKeys`]: https://html.spec.whatwg.org/multipage/#crossoriginownpropertykeys-(-o-)
|
||||
pub fn cross_origin_own_property_keys(
|
||||
pub(crate) fn cross_origin_own_property_keys(
|
||||
cx: SafeJSContext,
|
||||
_proxy: RawHandleObject,
|
||||
cross_origin_properties: &'static CrossOriginProperties,
|
||||
|
@ -276,7 +286,7 @@ pub fn cross_origin_own_property_keys(
|
|||
|
||||
/// # Safety
|
||||
/// `is_ordinary` must point to a valid, non-null bool.
|
||||
pub unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx(
|
||||
pub(crate) unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx(
|
||||
_: *mut JSContext,
|
||||
_proxy: RawHandleObject,
|
||||
is_ordinary: *mut bool,
|
||||
|
@ -294,7 +304,7 @@ pub unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx(
|
|||
///
|
||||
/// # Safety
|
||||
/// `result` must point to a valid, non-null ObjectOpResult.
|
||||
pub unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx(
|
||||
pub(crate) unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx(
|
||||
cx: *mut JSContext,
|
||||
proxy: RawHandleObject,
|
||||
proto: RawHandleObject,
|
||||
|
@ -323,25 +333,25 @@ pub unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx(
|
|||
true
|
||||
}
|
||||
|
||||
pub fn get_getter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
|
||||
pub(crate) fn get_getter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
|
||||
if d.hasGetter_() {
|
||||
out.set(d.getter_);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_setter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
|
||||
pub(crate) fn get_setter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
|
||||
if d.hasSetter_() {
|
||||
out.set(d.setter_);
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://tc39.es/ecma262/#sec-isaccessordescriptor>
|
||||
pub fn is_accessor_descriptor(d: &PropertyDescriptor) -> bool {
|
||||
pub(crate) fn is_accessor_descriptor(d: &PropertyDescriptor) -> bool {
|
||||
d.hasSetter_() || d.hasGetter_()
|
||||
}
|
||||
|
||||
/// <https://tc39.es/ecma262/#sec-isdatadescriptor>
|
||||
pub fn is_data_descriptor(d: &PropertyDescriptor) -> bool {
|
||||
pub(crate) fn is_data_descriptor(d: &PropertyDescriptor) -> bool {
|
||||
d.hasWritable_() || d.hasValue_()
|
||||
}
|
||||
|
||||
|
@ -353,7 +363,7 @@ pub fn is_data_descriptor(d: &PropertyDescriptor) -> bool {
|
|||
///
|
||||
/// # Safety
|
||||
/// `bp` must point to a valid, non-null bool.
|
||||
pub unsafe fn cross_origin_has_own(
|
||||
pub(crate) unsafe fn cross_origin_has_own(
|
||||
cx: SafeJSContext,
|
||||
_proxy: RawHandleObject,
|
||||
cross_origin_properties: &'static CrossOriginProperties,
|
||||
|
@ -379,7 +389,7 @@ pub unsafe fn cross_origin_has_own(
|
|||
/// for a maybe-cross-origin object.
|
||||
///
|
||||
/// [`CrossOriginGetOwnPropertyHelper`]: https://html.spec.whatwg.org/multipage/#crossorigingetownpropertyhelper-(-o,-p-)
|
||||
pub fn cross_origin_get_own_property_helper(
|
||||
pub(crate) fn cross_origin_get_own_property_helper(
|
||||
cx: SafeJSContext,
|
||||
proxy: RawHandleObject,
|
||||
cross_origin_properties: &'static CrossOriginProperties,
|
||||
|
@ -405,7 +415,7 @@ const ALLOWLISTED_SYMBOL_CODES: &[SymbolCode] = &[
|
|||
SymbolCode::isConcatSpreadable,
|
||||
];
|
||||
|
||||
pub fn is_cross_origin_allowlisted_prop(cx: SafeJSContext, id: RawHandleId) -> bool {
|
||||
pub(crate) fn is_cross_origin_allowlisted_prop(cx: SafeJSContext, id: RawHandleId) -> bool {
|
||||
unsafe {
|
||||
if jsid_to_string(*cx, Handle::from_raw(id)).is_some_and(|st| st == "then") {
|
||||
return true;
|
||||
|
@ -492,3 +502,269 @@ fn ensure_cross_origin_property_holder(
|
|||
|
||||
true
|
||||
}
|
||||
|
||||
/// 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<D: DomTypes>(
|
||||
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 = D::GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof));
|
||||
// TODO: include `id` and `access` in the exception message
|
||||
<D as DomHelpers<D>>::throw_dom_exception(cx, &global, Error::Security, CanGc::note());
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// 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<D: DomTypes>(
|
||||
cx: *mut JSContext,
|
||||
proxy: RawHandleObject,
|
||||
id: RawHandleId,
|
||||
v: RawHandleValue,
|
||||
receiver: RawHandleValue,
|
||||
result: *mut ObjectOpResult,
|
||||
) -> bool {
|
||||
let cx = SafeJSContext::from_ptr(cx);
|
||||
|
||||
if !<D as DomHelpers<D>>::is_platform_object_same_origin(cx, proxy) {
|
||||
return cross_origin_set::<D>(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,
|
||||
)
|
||||
}
|
||||
|
||||
/// Implementation of `[[GetPrototypeOf]]` for [`Location`].
|
||||
///
|
||||
/// [`Location`]: https://html.spec.whatwg.org/multipage/#location-getprototypeof
|
||||
pub(crate) unsafe fn maybe_cross_origin_get_prototype<D: 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 <D as DomHelpers<D>>::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 [`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<D: DomTypes>(
|
||||
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::<D>(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<D: DomTypes>(
|
||||
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::<D>(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
|
||||
}
|
||||
|
||||
/// 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<D: DomTypes>(
|
||||
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::<D>(cx, id, "access")
|
||||
}
|
||||
|
|
64
components/script_bindings/realms.rs
Normal file
64
components/script_bindings/realms.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
/* 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 js::jsapi::{GetCurrentRealmOrNull, JSAutoRealm};
|
||||
|
||||
use crate::DomTypes;
|
||||
use crate::interfaces::GlobalScopeHelpers;
|
||||
use crate::reflector::DomObject;
|
||||
use crate::script_runtime::JSContext;
|
||||
|
||||
pub struct AlreadyInRealm(());
|
||||
|
||||
impl AlreadyInRealm {
|
||||
#![allow(unsafe_code)]
|
||||
pub fn assert<D: DomTypes>() -> AlreadyInRealm {
|
||||
unsafe {
|
||||
assert!(!GetCurrentRealmOrNull(*D::GlobalScope::get_cx()).is_null());
|
||||
}
|
||||
AlreadyInRealm(())
|
||||
}
|
||||
|
||||
pub fn assert_for_cx(cx: JSContext) -> AlreadyInRealm {
|
||||
unsafe {
|
||||
assert!(!GetCurrentRealmOrNull(*cx).is_null());
|
||||
}
|
||||
AlreadyInRealm(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum InRealm<'a> {
|
||||
Already(&'a AlreadyInRealm),
|
||||
Entered(&'a JSAutoRealm),
|
||||
}
|
||||
|
||||
impl<'a> From<&'a AlreadyInRealm> for InRealm<'a> {
|
||||
fn from(token: &'a AlreadyInRealm) -> InRealm<'a> {
|
||||
InRealm::already(token)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a JSAutoRealm> for InRealm<'a> {
|
||||
fn from(token: &'a JSAutoRealm) -> InRealm<'a> {
|
||||
InRealm::entered(token)
|
||||
}
|
||||
}
|
||||
|
||||
impl InRealm<'_> {
|
||||
pub fn already(token: &AlreadyInRealm) -> InRealm {
|
||||
InRealm::Already(token)
|
||||
}
|
||||
|
||||
pub fn entered(token: &JSAutoRealm) -> InRealm {
|
||||
InRealm::Entered(token)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enter_realm<D: DomTypes>(object: &impl DomObject) -> JSAutoRealm {
|
||||
JSAutoRealm::new(
|
||||
*D::GlobalScope::get_cx(),
|
||||
object.reflector().get_jsobject().get(),
|
||||
)
|
||||
}
|
|
@ -6,6 +6,13 @@ use js::jsapi::{Heap, JSObject};
|
|||
use js::rust::HandleObject;
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
|
||||
use crate::interfaces::GlobalScopeHelpers;
|
||||
use crate::iterable::{Iterable, IterableIterator};
|
||||
use crate::realms::{AlreadyInRealm, InRealm};
|
||||
use crate::root::{Dom, DomRoot, Root};
|
||||
use crate::script_runtime::{CanGc, JSContext};
|
||||
use crate::{DomTypes, JSTraceable};
|
||||
|
||||
/// A struct to store a reference to the reflector of a DOM object.
|
||||
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||
#[derive(MallocSizeOf)]
|
||||
|
@ -90,3 +97,55 @@ impl MutDomObject for Reflector {
|
|||
self.set_jsobject(obj)
|
||||
}
|
||||
}
|
||||
|
||||
pub 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 {}
|
||||
|
||||
/// A trait to provide a function pointer to wrap function for DOM objects.
|
||||
pub 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 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>>>;
|
||||
}
|
||||
|
|
142
components/script_bindings/settings_stack.rs
Normal file
142
components/script_bindings/settings_stack.rs
Normal file
|
@ -0,0 +1,142 @@
|
|||
/* 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::marker::PhantomData;
|
||||
use std::thread;
|
||||
|
||||
use js::jsapi::{HideScriptedCaller, UnhideScriptedCaller};
|
||||
use js::rust::Runtime;
|
||||
|
||||
use crate::DomTypes;
|
||||
use crate::interfaces::{DomHelpers, GlobalScopeHelpers};
|
||||
use crate::root::{Dom, DomRoot};
|
||||
use crate::script_runtime::CanGc;
|
||||
|
||||
#[derive(Debug, Eq, JSTraceable, PartialEq)]
|
||||
pub enum StackEntryKind {
|
||||
Incumbent,
|
||||
Entry,
|
||||
}
|
||||
|
||||
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||
#[derive(JSTraceable)]
|
||||
pub struct StackEntry<D: DomTypes> {
|
||||
pub global: Dom<D::GlobalScope>,
|
||||
pub kind: StackEntryKind,
|
||||
}
|
||||
|
||||
/// RAII struct that pushes and pops entries from the script settings stack.
|
||||
pub 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 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() && D::GlobalScope::incumbent().is_none() {
|
||||
self.global.perform_a_microtask_checkpoint(CanGc::note());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// RAII struct that pushes and pops entries from the script settings stack.
|
||||
pub struct GenericAutoIncumbentScript<D: DomTypes> {
|
||||
global: usize,
|
||||
_marker: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<D: DomTypes> GenericAutoIncumbentScript<D> {
|
||||
/// <https://html.spec.whatwg.org/multipage/#prepare-to-run-a-callback>
|
||||
pub 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,16 +3,21 @@
|
|||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::cell::OnceCell;
|
||||
use std::fmt::Display;
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crossbeam_channel::Sender;
|
||||
use html5ever::interface::{Tracer as HtmlTracer, TreeSink};
|
||||
use html5ever::tokenizer::{TokenSink, Tokenizer};
|
||||
use html5ever::tree_builder::TreeBuilder;
|
||||
use indexmap::IndexMap;
|
||||
use js::gc::{GCMethods, Handle};
|
||||
use js::glue::CallObjectTracer;
|
||||
use js::jsapi::{GCTraceKindToAscii, Heap, JSObject, JSTracer, TraceKind};
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
use parking_lot::RwLock;
|
||||
use servo_arc::Arc as ServoArc;
|
||||
use smallvec::SmallVec;
|
||||
|
@ -46,7 +51,11 @@ pub unsafe fn trace_reflector(tracer: *mut JSTracer, description: &str, reflecto
|
|||
///
|
||||
/// # Safety
|
||||
/// tracer must point to a valid, non-null JS tracer.
|
||||
pub unsafe fn trace_object(tracer: *mut JSTracer, description: &str, obj: &Heap<*mut JSObject>) {
|
||||
pub(crate) unsafe fn trace_object(
|
||||
tracer: *mut JSTracer,
|
||||
description: &str,
|
||||
obj: &Heap<*mut JSObject>,
|
||||
) {
|
||||
unsafe {
|
||||
trace!("tracing {}", description);
|
||||
CallObjectTracer(
|
||||
|
@ -308,3 +317,101 @@ unsafe impl<Handle: JSTraceable + Clone, Sink: JSTraceable + XmlTreeSink<Handle
|
|||
tree_builder.sink.trace(trc);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 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 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 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 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()
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper type for nop traceble
|
||||
///
|
||||
/// SAFETY: Inner type must not impl JSTraceable
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
#[cfg_attr(crown, crown::trace_in_no_trace_lint::must_not_have_traceable)]
|
||||
pub(crate) struct NoTrace<T>(pub(crate) T);
|
||||
|
||||
impl<T: Display> Display for NoTrace<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for NoTrace<T> {
|
||||
fn from(item: T) -> Self {
|
||||
Self(item)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl<T> JSTraceable for NoTrace<T> {
|
||||
#[inline]
|
||||
unsafe fn trace(&self, _: *mut ::js::jsapi::JSTracer) {}
|
||||
}
|
||||
|
||||
impl<T: MallocSizeOf> MallocSizeOf for NoTrace<T> {
|
||||
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||
self.0.size_of(ops)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,17 +5,23 @@
|
|||
use std::ffi::CString;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::slice;
|
||||
|
||||
use js::conversions::ToJSValConvertible;
|
||||
use js::gc::Handle;
|
||||
use js::glue::{
|
||||
CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, JS_GetReservedSlot,
|
||||
RUST_FUNCTION_VALUE_TO_JITINFO,
|
||||
};
|
||||
use js::jsapi::{
|
||||
AtomToLinearString, CallArgs, ExceptionStackBehavior, GetLinearStringCharAt,
|
||||
GetLinearStringLength, GetNonCCWObjectGlobal, HandleObject as RawHandleObject, Heap,
|
||||
JS_ClearPendingException, JS_IsExceptionPending, JSAtom, JSContext, JSJitInfo, JSObject,
|
||||
JSTracer, MutableHandleValue as RawMutableHandleValue, ObjectOpResult, StringIsArrayIndex,
|
||||
GetLinearStringLength, GetNonCCWObjectGlobal, HandleId as RawHandleId,
|
||||
HandleObject as RawHandleObject, Heap, JS_ClearPendingException,
|
||||
JS_DeprecatedStringHasLatin1Chars, JS_EnumerateStandardClasses,
|
||||
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::{
|
||||
|
@ -29,11 +35,13 @@ use js::rust::{
|
|||
use js::{JS_CALLEE, rooted};
|
||||
use malloc_size_of::MallocSizeOfOps;
|
||||
|
||||
use crate::DomTypes;
|
||||
use crate::codegen::Globals::Globals;
|
||||
use crate::codegen::InheritTypes::TopTypeId;
|
||||
use crate::codegen::PrototypeList::{self, MAX_PROTO_CHAIN_LENGTH, PROTO_OR_IFACE_LENGTH};
|
||||
use crate::conversions::{PrototypeCheck, jsstring_to_str, private_from_proto_check};
|
||||
use crate::error::throw_invalid_this;
|
||||
use crate::interfaces::DomHelpers;
|
||||
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
||||
use crate::str::DOMString;
|
||||
use crate::trace::trace_object;
|
||||
|
@ -77,24 +85,24 @@ unsafe impl Sync for DOMJSClass {}
|
|||
|
||||
/// The index of the slot where the object holder of that interface's
|
||||
/// unforgeable members are defined.
|
||||
pub const DOM_PROTO_UNFORGEABLE_HOLDER_SLOT: u32 = 0;
|
||||
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 const DOM_PROTOTYPE_SLOT: u32 = js::JSCLASS_GLOBAL_SLOT_COUNT;
|
||||
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 const JSCLASS_DOM_GLOBAL: u32 = js::JSCLASS_USERBIT1;
|
||||
pub(crate) const JSCLASS_DOM_GLOBAL: u32 = js::JSCLASS_USERBIT1;
|
||||
|
||||
/// Returns the ProtoOrIfaceArray for the given global object.
|
||||
/// Fails if `global` is not a DOM global object.
|
||||
///
|
||||
/// # Safety
|
||||
/// `global` must point to a valid, non-null JS object.
|
||||
pub unsafe fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray {
|
||||
pub(crate) unsafe fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray {
|
||||
assert_ne!(((*get_object_class(global)).flags & JSCLASS_DOM_GLOBAL), 0);
|
||||
let mut slot = UndefinedValue();
|
||||
JS_GetReservedSlot(global, DOM_PROTOTYPE_SLOT, &mut slot);
|
||||
|
@ -112,7 +120,7 @@ pub type ProtoOrIfaceArray = [*mut JSObject; PROTO_OR_IFACE_LENGTH];
|
|||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
/// `found` must point to a valid, non-null bool.
|
||||
pub unsafe fn get_property_on_prototype(
|
||||
pub(crate) unsafe fn get_property_on_prototype(
|
||||
cx: *mut JSContext,
|
||||
proxy: HandleObject,
|
||||
receiver: HandleValue,
|
||||
|
@ -206,7 +214,7 @@ pub fn get_array_index_from_id(id: HandleId) -> Option<u32> {
|
|||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub unsafe fn find_enum_value<'a, T>(
|
||||
pub(crate) unsafe fn find_enum_value<'a, T>(
|
||||
cx: *mut JSContext,
|
||||
v: HandleValue,
|
||||
pairs: &'a [(&'static str, T)],
|
||||
|
@ -285,7 +293,7 @@ pub unsafe fn get_dictionary_property(
|
|||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub unsafe fn set_dictionary_property(
|
||||
pub(crate) unsafe fn set_dictionary_property(
|
||||
cx: *mut JSContext,
|
||||
object: HandleObject,
|
||||
property: &str,
|
||||
|
@ -325,7 +333,7 @@ pub unsafe fn has_property_on_prototype(
|
|||
///
|
||||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
pub unsafe fn delete_property_by_id(
|
||||
pub(crate) unsafe fn delete_property_by_id(
|
||||
cx: *mut JSContext,
|
||||
object: HandleObject,
|
||||
id: HandleId,
|
||||
|
@ -404,7 +412,7 @@ unsafe fn generic_call<const EXCEPTION_TO_REJECTION: bool>(
|
|||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
/// `vp` must point to a VALID, non-null JSVal.
|
||||
pub unsafe extern "C" fn generic_method<const EXCEPTION_TO_REJECTION: bool>(
|
||||
pub(crate) unsafe extern "C" fn generic_method<const EXCEPTION_TO_REJECTION: bool>(
|
||||
cx: *mut JSContext,
|
||||
argc: libc::c_uint,
|
||||
vp: *mut JSVal,
|
||||
|
@ -417,7 +425,7 @@ pub unsafe extern "C" fn generic_method<const EXCEPTION_TO_REJECTION: bool>(
|
|||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
/// `vp` must point to a VALID, non-null JSVal.
|
||||
pub unsafe extern "C" fn generic_getter<const EXCEPTION_TO_REJECTION: bool>(
|
||||
pub(crate) unsafe extern "C" fn generic_getter<const EXCEPTION_TO_REJECTION: bool>(
|
||||
cx: *mut JSContext,
|
||||
argc: libc::c_uint,
|
||||
vp: *mut JSVal,
|
||||
|
@ -430,7 +438,7 @@ pub unsafe extern "C" fn generic_getter<const EXCEPTION_TO_REJECTION: bool>(
|
|||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
/// `vp` must point to a VALID, non-null JSVal.
|
||||
pub unsafe extern "C" fn generic_lenient_getter<const EXCEPTION_TO_REJECTION: bool>(
|
||||
pub(crate) unsafe extern "C" fn generic_lenient_getter<const EXCEPTION_TO_REJECTION: bool>(
|
||||
cx: *mut JSContext,
|
||||
argc: libc::c_uint,
|
||||
vp: *mut JSVal,
|
||||
|
@ -458,7 +466,7 @@ unsafe extern "C" fn call_setter(
|
|||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
/// `vp` must point to a VALID, non-null JSVal.
|
||||
pub unsafe extern "C" fn generic_setter(
|
||||
pub(crate) unsafe extern "C" fn generic_setter(
|
||||
cx: *mut JSContext,
|
||||
argc: libc::c_uint,
|
||||
vp: *mut JSVal,
|
||||
|
@ -471,7 +479,7 @@ pub unsafe extern "C" fn generic_setter(
|
|||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
/// `vp` must point to a VALID, non-null JSVal.
|
||||
pub unsafe extern "C" fn generic_lenient_setter(
|
||||
pub(crate) unsafe extern "C" fn generic_lenient_setter(
|
||||
cx: *mut JSContext,
|
||||
argc: libc::c_uint,
|
||||
vp: *mut JSVal,
|
||||
|
@ -484,7 +492,7 @@ pub unsafe extern "C" fn generic_lenient_setter(
|
|||
///
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
/// `vp` must point to a VALID, non-null JSVal.
|
||||
pub unsafe extern "C" fn generic_static_promise_method(
|
||||
pub(crate) unsafe extern "C" fn generic_static_promise_method(
|
||||
cx: *mut JSContext,
|
||||
argc: libc::c_uint,
|
||||
vp: *mut JSVal,
|
||||
|
@ -508,7 +516,7 @@ pub unsafe extern "C" fn generic_static_promise_method(
|
|||
///
|
||||
/// # Safety
|
||||
/// `cx` must point to a valid, non-null JSContext.
|
||||
pub unsafe fn exception_to_promise(
|
||||
pub(crate) unsafe fn exception_to_promise(
|
||||
cx: *mut JSContext,
|
||||
rval: RawMutableHandleValue,
|
||||
_can_gc: CanGc,
|
||||
|
@ -533,7 +541,7 @@ pub unsafe fn exception_to_promise(
|
|||
/// # Safety
|
||||
/// `tracer` must point to a valid, non-null JSTracer.
|
||||
/// `obj` must point to a valid, non-null JSObject.
|
||||
pub unsafe fn trace_global(tracer: *mut JSTracer, obj: *mut JSObject) {
|
||||
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() {
|
||||
|
@ -557,7 +565,7 @@ impl<T> AsVoidPtr for T {
|
|||
}
|
||||
|
||||
// Generic method for returning c_char from caller
|
||||
pub trait AsCCharPtrPtr {
|
||||
pub(crate) trait AsCCharPtrPtr {
|
||||
fn as_c_char_ptr(&self) -> *const c_char;
|
||||
}
|
||||
|
||||
|
@ -566,3 +574,58 @@ impl AsCCharPtrPtr for [u8] {
|
|||
self as *const [u8] as *const c_char
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumerate lazy properties of a global object.
|
||||
pub(crate) unsafe extern "C" fn enumerate_global<D: DomTypes>(
|
||||
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 <D as DomHelpers<D>>::interface_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<D: DomTypes>(
|
||||
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) = <D as DomHelpers<D>>::interface_map().get(bytes) {
|
||||
init_fun(SafeJSContext::from_ptr(cx), Handle::from_raw(obj));
|
||||
*rval = true;
|
||||
} else {
|
||||
*rval = false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ use crate::root::DomRoot;
|
|||
/// stored for weak-referenceable bindings. We use slot 1 for holding it,
|
||||
/// this is unsafe for globals, we disallow weak-referenceable globals
|
||||
/// directly in codegen.
|
||||
pub const DOM_WEAK_SLOT: u32 = 1;
|
||||
pub(crate) const DOM_WEAK_SLOT: u32 = 1;
|
||||
|
||||
/// A weak reference to a JS-managed DOM object.
|
||||
#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue