use dom_struct::dom_struct;
use js::gc::HandleValue;
use js::jsapi::Heap;
use js::jsval::{JSVal, NullValue};
use js::rust::{HandleObject, MutableHandleValue};
use script_bindings::codegen::GenericBindings::PerformanceBinding::PerformanceMarkOptions;
use script_bindings::reflector::reflect_dom_object_with_proto_and_cx;
use servo_base::cross_process_instant::CrossProcessInstant;
use time::Duration;
use crate::dom::PERFORMANCE_TIMING_ATTRIBUTES;
use crate::dom::bindings::codegen::Bindings::PerformanceMarkBinding::PerformanceMarkMethods;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::globalscope::GlobalScope;
use crate::dom::performance::performanceentry::{EntryType, PerformanceEntry};
use crate::dom::window::Window;
#[dom_struct]
pub(crate) struct PerformanceMark {
entry: PerformanceEntry,
#[ignore_malloc_size_of = "Defined in rust-mozjs"]
detail: Heap<JSVal>,
}
impl PerformanceMark {
fn new_inherited(
name: DOMString,
start_time: CrossProcessInstant,
duration: Duration,
) -> PerformanceMark {
PerformanceMark {
entry: PerformanceEntry::new_inherited(
name,
EntryType::Mark,
Some(start_time),
duration,
),
detail: Default::default(),
}
}
fn set_detail(&self, handle: HandleValue<'_>) {
self.detail.set(handle.get());
}
pub(crate) fn new_with_proto(
cx: &mut js::context::JSContext,
global: &GlobalScope,
proto: Option<HandleObject>,
name: DOMString,
start_time: CrossProcessInstant,
duration: Duration,
) -> DomRoot<PerformanceMark> {
reflect_dom_object_with_proto_and_cx(
Box::new(PerformanceMark::new_inherited(name, start_time, duration)),
global,
proto,
cx,
)
}
}
impl PerformanceMarkMethods<crate::DomTypeHolder> for PerformanceMark {
fn Detail(&self, mut retval: MutableHandleValue) {
retval.set(self.detail.get())
}
fn Constructor(
cx: &mut js::context::JSContext,
global: &GlobalScope,
proto: Option<HandleObject>,
mark_name: DOMString,
mark_options: RootedTraceableBox<PerformanceMarkOptions>,
) -> Fallible<DomRoot<PerformanceMark>> {
if global.is::<Window>() && PERFORMANCE_TIMING_ATTRIBUTES.contains(&&*mark_name.str()) {
return Err(Error::Syntax(Some(
"Read-only attribute cannot be used as a mark name".to_owned(),
)));
}
let start_time = match mark_options.startTime {
Some(start_time) => {
if start_time.is_sign_negative() {
return Err(Error::Type(c"startTime must not be negative".to_owned()));
}
global.performance().time_origin() +
Duration::microseconds(start_time.mul_add(1000.0, 0.0) as i64)
},
None => CrossProcessInstant::now(),
};
let entry = PerformanceMark::new_with_proto(
cx,
global,
proto,
mark_name,
start_time,
Duration::ZERO,
);
rooted!(&in(cx) let mut detail = NullValue());
if !mark_options.detail.get().is_null_or_undefined() {
let record = structuredclone::write(cx, mark_options.detail.handle(), None)?;
structuredclone::read(cx, global, record, detail.handle_mut())?;
}
entry.set_detail(detail.handle());
Ok(entry)
}
}