use dom_struct::dom_struct;
use js::context::JSContext;
use js::jsapi::{Heap, JSObject};
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
use script_bindings::error::ErrorResult;
use script_bindings::reflector::{Reflector, reflect_dom_object};
use script_bindings::root::DomRoot;
use script_bindings::script_runtime::CanGc;
use servo_base::generic_channel::GenericSender;
use servo_base::id::ServiceWorkerId;
use servo_constellation_traits::ServiceWorkerMsg;
use servo_url::ServoUrl;
use crate::dom::bindings::codegen::Bindings::ClientBinding::{ClientMethods, FrameType};
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::reflector::DomGlobal;
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::globalscope::GlobalScope;
#[dom_struct]
pub(crate) struct Client {
reflector_: Reflector,
#[no_trace]
swmanager_sender: GenericSender<ServiceWorkerMsg>,
#[no_trace]
url: ServoUrl,
frame_type: FrameType,
#[no_trace]
worker_id: ServiceWorkerId,
}
impl Client {
fn new_inherited(
swmanager_sender: GenericSender<ServiceWorkerMsg>,
url: ServoUrl,
frame_type: FrameType,
worker_id: ServiceWorkerId,
) -> Client {
Client {
reflector_: Reflector::new(),
swmanager_sender,
url,
frame_type,
worker_id,
}
}
pub(crate) fn new(
global: &GlobalScope,
swmanager_sender: GenericSender<ServiceWorkerMsg>,
url: ServoUrl,
frame_type: FrameType,
worker_id: ServiceWorkerId,
can_gc: CanGc,
) -> DomRoot<Client> {
reflect_dom_object(
Box::new(Client::new_inherited(
swmanager_sender,
url,
frame_type,
worker_id,
)),
global,
can_gc,
)
}
fn post_message_impl(
&self,
cx: &mut JSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
let global = self.reflector_.global();
let origin = global.origin();
let data = structuredclone::write(cx, message, Some(transfer))?;
self.swmanager_sender
.send(ServiceWorkerMsg::ForwardWorkerMessage {
data,
source: self.worker_id,
url: self.url.clone(),
origin: origin.immutable().clone(),
})
.map_err(|_| {
Error::Type(c"Failed to send message to service worker manager".to_owned())
})
}
}
impl ClientMethods<crate::DomTypeHolder> for Client {
fn PostMessage(
&self,
cx: &mut JSContext,
message: HandleValue,
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
) -> ErrorResult {
self.post_message_impl(cx, message, transfer)
}
fn PostMessage_(
&self,
cx: &mut JSContext,
message: HandleValue,
options: RootedTraceableBox<StructuredSerializeOptions>,
) -> ErrorResult {
let mut rooted = CustomAutoRooter::new(
options
.transfer
.iter()
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
.collect(),
);
#[expect(unsafe_code)]
let guard = unsafe { CustomAutoRooterGuard::new(cx.raw_cx(), &mut rooted) };
self.post_message_impl(cx, message, guard)
}
fn Url(&self) -> USVString {
USVString(self.url.as_str().to_owned())
}
fn FrameType(&self) -> FrameType {
self.frame_type
}
fn Id(&self) -> DOMString {
format!("{}", self.worker_id).into()
}
}