1use std::borrow::ToOwned;
6use std::cell::Cell;
7use std::ptr::{self, NonNull};
8
9use base::generic_channel::{LazyCallback, lazy_callback};
10use constellation_traits::BlobImpl;
11use dom_struct::dom_struct;
12use ipc_channel::router::ROUTER;
13use js::jsapi::JSObject;
14use js::jsval::UndefinedValue;
15use js::realm::AutoRealm;
16use js::rust::{CustomAutoRooterGuard, HandleObject};
17use js::typedarray::{ArrayBuffer, ArrayBufferView, CreateWith};
18use net_traits::request::{
19 CacheMode, CredentialsMode, RedirectMode, Referrer, RequestBuilder, RequestMode,
20 ServiceWorkersMode,
21};
22use net_traits::{
23 CoreResourceMsg, FetchChannels, MessageData, WebSocketDomAction, WebSocketNetworkEvent,
24};
25use profile_traits::ipc as ProfiledIpc;
26use script_bindings::conversions::SafeToJSValConvertible;
27use servo_url::{ImmutableOrigin, ServoUrl};
28
29use crate::dom::bindings::cell::DomRefCell;
30use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
31use crate::dom::bindings::codegen::Bindings::WebSocketBinding::{BinaryType, WebSocketMethods};
32use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
33use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence;
34use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
35use crate::dom::bindings::inheritance::Castable;
36use crate::dom::bindings::refcounted::Trusted;
37use crate::dom::bindings::reflector::{DomGlobal, DomObject, reflect_dom_object_with_proto};
38use crate::dom::bindings::root::DomRoot;
39use crate::dom::bindings::str::{DOMString, USVString, is_token};
40use crate::dom::blob::Blob;
41use crate::dom::closeevent::CloseEvent;
42use crate::dom::csp::{GlobalCspReporting, Violation};
43use crate::dom::event::{Event, EventBubbles, EventCancelable};
44use crate::dom::eventtarget::EventTarget;
45use crate::dom::globalscope::GlobalScope;
46use crate::dom::messageevent::MessageEvent;
47use crate::dom::window::Window;
48use crate::fetch::RequestWithGlobalScope;
49use crate::script_runtime::CanGc;
50use crate::task::TaskOnce;
51use crate::task_source::SendableTaskSource;
52
53#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
54enum WebSocketRequestState {
55 Connecting = 0,
56 Open = 1,
57 Closing = 2,
58 Closed = 3,
59}
60
61#[expect(dead_code)]
64mod close_code {
65 pub(crate) const NORMAL: u16 = 1000;
66 pub(crate) const GOING_AWAY: u16 = 1001;
67 pub(crate) const PROTOCOL_ERROR: u16 = 1002;
68 pub(crate) const UNSUPPORTED_DATATYPE: u16 = 1003;
69 pub(crate) const NO_STATUS: u16 = 1005;
70 pub(crate) const ABNORMAL: u16 = 1006;
71 pub(crate) const INVALID_PAYLOAD: u16 = 1007;
72 pub(crate) const POLICY_VIOLATION: u16 = 1008;
73 pub(crate) const TOO_LARGE: u16 = 1009;
74 pub(crate) const EXTENSION_MISSING: u16 = 1010;
75 pub(crate) const INTERNAL_ERROR: u16 = 1011;
76 pub(crate) const TLS_FAILED: u16 = 1015;
77}
78
79fn close_the_websocket_connection(
80 address: Trusted<WebSocket>,
81 task_source: &SendableTaskSource,
82 code: Option<u16>,
83 reason: String,
84) {
85 task_source.queue(CloseTask {
86 address,
87 failed: false,
88 code,
89 reason: Some(reason),
90 });
91}
92
93fn fail_the_websocket_connection(address: Trusted<WebSocket>, task_source: &SendableTaskSource) {
94 task_source.queue(CloseTask {
95 address,
96 failed: true,
97 code: Some(close_code::ABNORMAL),
98 reason: None,
99 });
100}
101
102#[dom_struct]
103pub(crate) struct WebSocket {
104 eventtarget: EventTarget,
105 #[no_trace]
106 url: ServoUrl,
107 ready_state: Cell<WebSocketRequestState>,
108 buffered_amount: Cell<u64>,
109 clearing_buffer: Cell<bool>, #[no_trace]
111 callback: LazyCallback<WebSocketDomAction>,
112 binary_type: Cell<BinaryType>,
113 protocol: DomRefCell<String>, }
115
116impl WebSocket {
117 fn new_inherited(url: ServoUrl, callback: LazyCallback<WebSocketDomAction>) -> WebSocket {
118 WebSocket {
119 eventtarget: EventTarget::new_inherited(),
120 url,
121 ready_state: Cell::new(WebSocketRequestState::Connecting),
122 buffered_amount: Cell::new(0),
123 clearing_buffer: Cell::new(false),
124 callback,
125 binary_type: Cell::new(BinaryType::Blob),
126 protocol: DomRefCell::new("".to_owned()),
127 }
128 }
129
130 fn new(
131 global: &GlobalScope,
132 proto: Option<HandleObject>,
133 url: ServoUrl,
134 sender: LazyCallback<WebSocketDomAction>,
135 can_gc: CanGc,
136 ) -> DomRoot<WebSocket> {
137 let websocket = reflect_dom_object_with_proto(
138 Box::new(WebSocket::new_inherited(url, sender)),
139 global,
140 proto,
141 can_gc,
142 );
143 if let Some(window) = global.downcast::<Window>() {
144 window.Document().track_websocket(&websocket);
145 }
146 websocket
147 }
148
149 fn send_impl(&self, data_byte_len: u64) -> Fallible<bool> {
151 let return_after_buffer = match self.ready_state.get() {
152 WebSocketRequestState::Connecting => {
153 return Err(Error::InvalidState(None));
154 },
155 WebSocketRequestState::Open => false,
156 WebSocketRequestState::Closing | WebSocketRequestState::Closed => true,
157 };
158
159 let address = Trusted::new(self);
160
161 match data_byte_len.checked_add(self.buffered_amount.get()) {
162 None => panic!(),
163 Some(new_amount) => self.buffered_amount.set(new_amount),
164 };
165
166 if return_after_buffer {
167 return Ok(false);
168 }
169
170 if !self.clearing_buffer.get() && self.ready_state.get() == WebSocketRequestState::Open {
171 self.clearing_buffer.set(true);
172
173 self.global()
175 .task_manager()
176 .websocket_task_source()
177 .queue_unconditionally(BufferedAmountTask { address });
178 }
179
180 Ok(true)
181 }
182
183 pub(crate) fn origin(&self) -> ImmutableOrigin {
184 self.url.origin()
185 }
186
187 pub(crate) fn make_disappear(&self) -> bool {
190 let result = self.ready_state.get() != WebSocketRequestState::Closed;
191 let _ = self.Close(Some(1001), None);
192 result
193 }
194}
195
196impl WebSocketMethods<crate::DomTypeHolder> for WebSocket {
197 fn Constructor(
199 global: &GlobalScope,
200 proto: Option<HandleObject>,
201 can_gc: CanGc,
202 url: DOMString,
203 protocols: Option<StringOrStringSequence>,
204 ) -> Fallible<DomRoot<WebSocket>> {
205 let base_url = global.api_base_url();
207 let mut url_record =
210 ServoUrl::parse_with_base(Some(&base_url), &url.str()).or(Err(Error::Syntax(None)))?;
211
212 match url_record.scheme() {
216 "http" => {
217 url_record
218 .as_mut_url()
219 .set_scheme("ws")
220 .expect("Can't set scheme from http to ws");
221 },
222 "https" => {
223 url_record
224 .as_mut_url()
225 .set_scheme("wss")
226 .expect("Can't set scheme from https to wss");
227 },
228 "ws" | "wss" => {},
229 _ => return Err(Error::Syntax(None)),
230 }
231
232 if url_record.fragment().is_some() {
234 return Err(Error::Syntax(None));
235 }
236
237 let protocols = protocols.map_or(vec![], |p| match p {
239 StringOrStringSequence::String(string) => vec![string.into()],
240 StringOrStringSequence::StringSequence(seq) => {
241 seq.into_iter().map(String::from).collect()
242 },
243 });
244
245 for (i, protocol) in protocols.iter().enumerate() {
249 if protocols[i + 1..]
253 .iter()
254 .any(|p| p.eq_ignore_ascii_case(protocol))
255 {
256 return Err(Error::Syntax(None));
257 }
258
259 if !is_token(protocol.as_bytes()) {
261 return Err(Error::Syntax(None));
262 }
263 }
264
265 let (dom_action_sender, resource_action_receiver) = lazy_callback();
267 let (resource_event_sender, dom_event_receiver) =
268 ProfiledIpc::channel(global.time_profiler_chan().clone()).unwrap();
269
270 let ws = WebSocket::new(global, proto, url_record.clone(), dom_action_sender, can_gc);
272 let address = Trusted::new(&*ws);
273
274 let request = RequestBuilder::new(
280 global.webview_id(),
281 url_record.clone(),
282 Referrer::NoReferrer,
283 )
284 .with_global_scope(global)
285 .mode(RequestMode::WebSocket {
286 protocols,
287 original_url: url_record,
288 })
289 .service_workers_mode(ServiceWorkersMode::None)
290 .credentials_mode(CredentialsMode::Include)
291 .cache_mode(CacheMode::NoCache)
292 .redirect_mode(RedirectMode::Error);
293
294 let channels = FetchChannels::WebSocket {
295 event_sender: resource_event_sender,
296 action_receiver: resource_action_receiver,
297 };
298 let _ = global
299 .core_resource_thread()
300 .send(CoreResourceMsg::Fetch(request, channels));
301
302 let task_source = global.task_manager().websocket_task_source().to_sendable();
303 ROUTER.add_typed_route(
304 dom_event_receiver.to_ipc_receiver(),
305 Box::new(move |message| match message.unwrap() {
306 WebSocketNetworkEvent::ReportCSPViolations(violations) => {
307 let task = ReportCSPViolationTask {
308 websocket: address.clone(),
309 violations,
310 };
311 task_source.queue(task);
312 },
313 WebSocketNetworkEvent::ConnectionEstablished { protocol_in_use } => {
314 let open_thread = ConnectionEstablishedTask {
315 address: address.clone(),
316 protocol_in_use,
317 };
318 task_source.queue(open_thread);
319 },
320 WebSocketNetworkEvent::MessageReceived(message) => {
321 let message_thread = MessageReceivedTask {
322 address: address.clone(),
323 message,
324 };
325 task_source.queue(message_thread);
326 },
327 WebSocketNetworkEvent::Fail => {
328 fail_the_websocket_connection(address.clone(), &task_source);
329 },
330 WebSocketNetworkEvent::Close(code, reason) => {
331 close_the_websocket_connection(address.clone(), &task_source, code, reason);
332 },
333 }),
334 );
335
336 Ok(ws)
337 }
338
339 event_handler!(open, GetOnopen, SetOnopen);
341
342 event_handler!(close, GetOnclose, SetOnclose);
344
345 event_handler!(error, GetOnerror, SetOnerror);
347
348 event_handler!(message, GetOnmessage, SetOnmessage);
350
351 fn Url(&self) -> DOMString {
353 DOMString::from(self.url.as_str())
354 }
355
356 fn ReadyState(&self) -> u16 {
358 self.ready_state.get() as u16
359 }
360
361 fn BufferedAmount(&self) -> u64 {
363 self.buffered_amount.get()
364 }
365
366 fn BinaryType(&self) -> BinaryType {
368 self.binary_type.get()
369 }
370
371 fn SetBinaryType(&self, btype: BinaryType) {
373 self.binary_type.set(btype)
374 }
375
376 fn Protocol(&self) -> DOMString {
378 DOMString::from(self.protocol.borrow().clone())
379 }
380
381 fn Send(&self, data: USVString) -> ErrorResult {
383 let data_byte_len = data.0.len() as u64;
384 let send_data = self.send_impl(data_byte_len)?;
385
386 if send_data {
387 let _ = self
388 .callback
389 .send(WebSocketDomAction::SendMessage(MessageData::Text(data.0)));
390 }
391
392 Ok(())
393 }
394
395 fn Send_(&self, blob: &Blob) -> ErrorResult {
397 let data_byte_len = blob.Size();
402 let send_data = self.send_impl(data_byte_len)?;
403
404 if send_data {
405 let bytes = blob.get_bytes().unwrap_or_default();
406 let _ = self
407 .callback
408 .send(WebSocketDomAction::SendMessage(MessageData::Binary(bytes)));
409 }
410
411 Ok(())
412 }
413
414 fn Send__(&self, array: CustomAutoRooterGuard<ArrayBuffer>) -> ErrorResult {
416 let bytes = array.to_vec();
417 let data_byte_len = bytes.len();
418 let send_data = self.send_impl(data_byte_len as u64)?;
419
420 if send_data {
421 let _ = self
422 .callback
423 .send(WebSocketDomAction::SendMessage(MessageData::Binary(bytes)));
424 }
425 Ok(())
426 }
427
428 fn Send___(&self, array: CustomAutoRooterGuard<ArrayBufferView>) -> ErrorResult {
430 let bytes = array.to_vec();
431 let data_byte_len = bytes.len();
432 let send_data = self.send_impl(data_byte_len as u64)?;
433
434 if send_data {
435 let _ = self
436 .callback
437 .send(WebSocketDomAction::SendMessage(MessageData::Binary(bytes)));
438 }
439 Ok(())
440 }
441
442 fn Close(&self, code: Option<u16>, reason: Option<USVString>) -> ErrorResult {
444 if let Some(code) = code {
445 if code != close_code::NORMAL && !(3000..=4999).contains(&code) {
447 return Err(Error::InvalidAccess(None));
448 }
449 }
450 if let Some(ref reason) = reason {
451 if reason.0.len() > 123 {
452 return Err(Error::Syntax(Some("Reason too long".to_string())));
454 }
455 }
456
457 match self.ready_state.get() {
458 WebSocketRequestState::Closing | WebSocketRequestState::Closed => {}, WebSocketRequestState::Connecting => {
460 self.ready_state.set(WebSocketRequestState::Closing);
464
465 fail_the_websocket_connection(
466 Trusted::new(self),
467 &self
468 .global()
469 .task_manager()
470 .websocket_task_source()
471 .to_sendable(),
472 );
473 },
474 WebSocketRequestState::Open => {
475 self.ready_state.set(WebSocketRequestState::Closing);
476
477 let reason = reason.map(|reason| reason.0);
480 let _ = self.callback.send(WebSocketDomAction::Close(code, reason));
481 },
482 }
483 Ok(()) }
485}
486
487struct ReportCSPViolationTask {
488 websocket: Trusted<WebSocket>,
489 violations: Vec<Violation>,
490}
491
492impl TaskOnce for ReportCSPViolationTask {
493 fn run_once(self, _cx: &mut js::context::JSContext) {
494 let global = self.websocket.root().global();
495 global.report_csp_violations(self.violations, None, None);
496 }
497}
498
499struct ConnectionEstablishedTask {
502 address: Trusted<WebSocket>,
503 protocol_in_use: Option<String>,
504}
505
506impl TaskOnce for ConnectionEstablishedTask {
507 fn run_once(self, cx: &mut js::context::JSContext) {
509 let ws = self.address.root();
510
511 ws.ready_state.set(WebSocketRequestState::Open);
513
514 if let Some(protocol_name) = self.protocol_in_use {
519 *ws.protocol.borrow_mut() = protocol_name;
520 };
521
522 ws.upcast().fire_event(atom!("open"), CanGc::from_cx(cx));
524 }
525}
526
527struct BufferedAmountTask {
528 address: Trusted<WebSocket>,
529}
530
531impl TaskOnce for BufferedAmountTask {
532 fn run_once(self, _cx: &mut js::context::JSContext) {
538 let ws = self.address.root();
539
540 ws.buffered_amount.set(0);
541 ws.clearing_buffer.set(false);
542 }
543}
544
545struct CloseTask {
546 address: Trusted<WebSocket>,
547 failed: bool,
548 code: Option<u16>,
549 reason: Option<String>,
550}
551
552impl TaskOnce for CloseTask {
553 fn run_once(self, cx: &mut js::context::JSContext) {
554 let ws = self.address.root();
555
556 if ws.ready_state.get() == WebSocketRequestState::Closed {
557 return;
559 }
560
561 ws.ready_state.set(WebSocketRequestState::Closed);
566
567 if self.failed {
569 ws.upcast().fire_event(atom!("error"), CanGc::from_cx(cx));
570 }
571
572 let clean_close = !self.failed;
574 let code = self.code.unwrap_or(close_code::NO_STATUS);
575 let reason = DOMString::from(self.reason.unwrap_or("".to_owned()));
576 let close_event = CloseEvent::new(
577 &ws.global(),
578 atom!("close"),
579 EventBubbles::DoesNotBubble,
580 EventCancelable::NotCancelable,
581 clean_close,
582 code,
583 reason,
584 CanGc::from_cx(cx),
585 );
586 close_event
587 .upcast::<Event>()
588 .fire(ws.upcast(), CanGc::from_cx(cx));
589 }
590}
591
592struct MessageReceivedTask {
593 address: Trusted<WebSocket>,
594 message: MessageData,
595}
596
597impl TaskOnce for MessageReceivedTask {
598 #[expect(unsafe_code)]
599 fn run_once(self, cx: &mut js::context::JSContext) {
600 let ws = self.address.root();
601 debug!(
602 "MessageReceivedTask::handler({:p}): readyState={:?}",
603 &*ws,
604 ws.ready_state.get()
605 );
606
607 if ws.ready_state.get() != WebSocketRequestState::Open {
609 return;
610 }
611
612 let global = ws.global();
614 let mut realm = AutoRealm::new(
615 cx,
616 NonNull::new(ws.reflector().get_jsobject().get()).unwrap(),
617 );
618 let cx = &mut *realm;
619 rooted!(&in(cx) let mut message = UndefinedValue());
620 match self.message {
621 MessageData::Text(text) => {
622 text.safe_to_jsval(cx.into(), message.handle_mut(), CanGc::from_cx(cx))
623 },
624 MessageData::Binary(data) => match ws.binary_type.get() {
625 BinaryType::Blob => {
626 let blob = Blob::new(
627 &global,
628 BlobImpl::new_from_bytes(data, "".to_owned()),
629 CanGc::from_cx(cx),
630 );
631 blob.safe_to_jsval(cx.into(), message.handle_mut(), CanGc::from_cx(cx));
632 },
633 BinaryType::Arraybuffer => {
634 rooted!(&in(cx) let mut array_buffer = ptr::null_mut::<JSObject>());
635 unsafe {
637 assert!(
638 ArrayBuffer::create(
639 cx.raw_cx(),
640 CreateWith::Slice(&data),
641 array_buffer.handle_mut()
642 )
643 .is_ok()
644 )
645 };
646
647 (*array_buffer).safe_to_jsval(
648 cx.into(),
649 message.handle_mut(),
650 CanGc::from_cx(cx),
651 );
652 },
653 },
654 }
655 MessageEvent::dispatch_jsval(
656 ws.upcast(),
657 &global,
658 message.handle(),
659 Some(&ws.origin().ascii_serialization()),
660 None,
661 vec![],
662 CanGc::from_cx(cx),
663 );
664 }
665}