script/dom/
websocket.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::borrow::ToOwned;
6use std::cell::Cell;
7use std::ptr::{self, NonNull};
8
9use constellation_traits::BlobImpl;
10use dom_struct::dom_struct;
11use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
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// Close codes defined in https://tools.ietf.org/html/rfc6455#section-7.4.1
62// Names are from https://github.com/mozilla/gecko-dev/blob/master/netwerk/protocol/websocket/nsIWebSocketChannel.idl
63#[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>, // Flag to tell if there is a running thread to clear buffered_amount
110    #[no_trace]
111    sender: IpcSender<WebSocketDomAction>,
112    binary_type: Cell<BinaryType>,
113    protocol: DomRefCell<String>, // Subprotocol selected by server
114}
115
116impl WebSocket {
117    fn new_inherited(url: ServoUrl, sender: IpcSender<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            sender,
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: IpcSender<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    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-send>
150    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            // TODO(mrobinson): Should this task be cancellable?
174            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    /// <https://websockets.spec.whatwg.org/#make-disappear>
188    /// Returns true if any action was taken.
189    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    /// <https://html.spec.whatwg.org/multipage/#dom-websocket>
198    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        // Step 1. Let baseURL be this's relevant settings object's API base URL.
206        let base_url = global.api_base_url();
207        // Step 2. Let urlRecord be the result of applying the URL parser to url with baseURL.
208        // Step 3. If urlRecord is failure, then throw a "SyntaxError" DOMException.
209        let mut url_record =
210            ServoUrl::parse_with_base(Some(&base_url), &url.str()).or(Err(Error::Syntax(None)))?;
211
212        // Step 4. If urlRecord’s scheme is "http", then set urlRecord’s scheme to "ws".
213        // Step 5. Otherwise, if urlRecord’s scheme is "https", set urlRecord’s scheme to "wss".
214        // Step 6. If urlRecord’s scheme is not "ws" or "wss", then throw a "SyntaxError" DOMException.
215        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        // Step 7. If urlRecord’s fragment is non-null, then throw a "SyntaxError" DOMException.
233        if url_record.fragment().is_some() {
234            return Err(Error::Syntax(None));
235        }
236
237        // Step 8. If protocols is a string, set protocols to a sequence consisting of just that string.
238        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        // Step 9. If any of the values in protocols occur more than once or otherwise fail to match the requirements
246        // for elements that comprise the value of `Sec-WebSocket-Protocol` fields as defined by The WebSocket protocol,
247        // then throw a "SyntaxError" DOMException.
248        for (i, protocol) in protocols.iter().enumerate() {
249            // https://tools.ietf.org/html/rfc6455#section-4.1
250            // Handshake requirements, step 10
251
252            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            // https://tools.ietf.org/html/rfc6455#section-4.1
260            if !is_token(protocol.as_bytes()) {
261                return Err(Error::Syntax(None));
262            }
263        }
264
265        // Create the interface for communication with the resource thread
266        let (dom_action_sender, resource_action_receiver): (
267            IpcSender<WebSocketDomAction>,
268            IpcReceiver<WebSocketDomAction>,
269        ) = ipc::channel().unwrap();
270        let (resource_event_sender, dom_event_receiver): (
271            IpcSender<WebSocketNetworkEvent>,
272            ProfiledIpc::IpcReceiver<WebSocketNetworkEvent>,
273        ) = ProfiledIpc::channel(global.time_profiler_chan().clone()).unwrap();
274
275        // Step 12. Establish a WebSocket connection given urlRecord, protocols, and client.
276        let ws = WebSocket::new(global, proto, url_record.clone(), dom_action_sender, can_gc);
277        let address = Trusted::new(&*ws);
278
279        // https://websockets.spec.whatwg.org/#concept-websocket-establish
280        //
281        // Let request be a new request, whose URL is requestURL, client is client, service-workers
282        // mode is "none", referrer is "no-referrer", mode is "websocket", credentials mode is
283        // "include", cache mode is "no-store" , and redirect mode is "error"
284        let request = RequestBuilder::new(
285            global.webview_id(),
286            url_record.clone(),
287            Referrer::NoReferrer,
288        )
289        .with_global_scope(global)
290        .mode(RequestMode::WebSocket {
291            protocols,
292            original_url: url_record,
293        })
294        .service_workers_mode(ServiceWorkersMode::None)
295        .credentials_mode(CredentialsMode::Include)
296        .cache_mode(CacheMode::NoCache)
297        .redirect_mode(RedirectMode::Error);
298
299        let channels = FetchChannels::WebSocket {
300            event_sender: resource_event_sender,
301            action_receiver: resource_action_receiver,
302        };
303        let _ = global
304            .core_resource_thread()
305            .send(CoreResourceMsg::Fetch(request, channels));
306
307        let task_source = global.task_manager().websocket_task_source().to_sendable();
308        ROUTER.add_typed_route(
309            dom_event_receiver.to_ipc_receiver(),
310            Box::new(move |message| match message.unwrap() {
311                WebSocketNetworkEvent::ReportCSPViolations(violations) => {
312                    let task = ReportCSPViolationTask {
313                        websocket: address.clone(),
314                        violations,
315                    };
316                    task_source.queue(task);
317                },
318                WebSocketNetworkEvent::ConnectionEstablished { protocol_in_use } => {
319                    let open_thread = ConnectionEstablishedTask {
320                        address: address.clone(),
321                        protocol_in_use,
322                    };
323                    task_source.queue(open_thread);
324                },
325                WebSocketNetworkEvent::MessageReceived(message) => {
326                    let message_thread = MessageReceivedTask {
327                        address: address.clone(),
328                        message,
329                    };
330                    task_source.queue(message_thread);
331                },
332                WebSocketNetworkEvent::Fail => {
333                    fail_the_websocket_connection(address.clone(), &task_source);
334                },
335                WebSocketNetworkEvent::Close(code, reason) => {
336                    close_the_websocket_connection(address.clone(), &task_source, code, reason);
337                },
338            }),
339        );
340
341        Ok(ws)
342    }
343
344    // https://html.spec.whatwg.org/multipage/#handler-websocket-onopen
345    event_handler!(open, GetOnopen, SetOnopen);
346
347    // https://html.spec.whatwg.org/multipage/#handler-websocket-onclose
348    event_handler!(close, GetOnclose, SetOnclose);
349
350    // https://html.spec.whatwg.org/multipage/#handler-websocket-onerror
351    event_handler!(error, GetOnerror, SetOnerror);
352
353    // https://html.spec.whatwg.org/multipage/#handler-websocket-onmessage
354    event_handler!(message, GetOnmessage, SetOnmessage);
355
356    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-url>
357    fn Url(&self) -> DOMString {
358        DOMString::from(self.url.as_str())
359    }
360
361    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-readystate>
362    fn ReadyState(&self) -> u16 {
363        self.ready_state.get() as u16
364    }
365
366    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-bufferedamount>
367    fn BufferedAmount(&self) -> u64 {
368        self.buffered_amount.get()
369    }
370
371    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-binarytype>
372    fn BinaryType(&self) -> BinaryType {
373        self.binary_type.get()
374    }
375
376    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-binarytype>
377    fn SetBinaryType(&self, btype: BinaryType) {
378        self.binary_type.set(btype)
379    }
380
381    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-protocol>
382    fn Protocol(&self) -> DOMString {
383        DOMString::from(self.protocol.borrow().clone())
384    }
385
386    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-send>
387    fn Send(&self, data: USVString) -> ErrorResult {
388        let data_byte_len = data.0.len() as u64;
389        let send_data = self.send_impl(data_byte_len)?;
390
391        if send_data {
392            let _ = self
393                .sender
394                .send(WebSocketDomAction::SendMessage(MessageData::Text(data.0)));
395        }
396
397        Ok(())
398    }
399
400    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-send>
401    fn Send_(&self, blob: &Blob) -> ErrorResult {
402        /* As per https://html.spec.whatwg.org/multipage/#websocket
403           the buffered amount needs to be clamped to u32, even though Blob.Size() is u64
404           If the buffer limit is reached in the first place, there are likely other major problems
405        */
406        let data_byte_len = blob.Size();
407        let send_data = self.send_impl(data_byte_len)?;
408
409        if send_data {
410            let bytes = blob.get_bytes().unwrap_or_default();
411            let _ = self
412                .sender
413                .send(WebSocketDomAction::SendMessage(MessageData::Binary(bytes)));
414        }
415
416        Ok(())
417    }
418
419    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-send>
420    fn Send__(&self, array: CustomAutoRooterGuard<ArrayBuffer>) -> ErrorResult {
421        let bytes = array.to_vec();
422        let data_byte_len = bytes.len();
423        let send_data = self.send_impl(data_byte_len as u64)?;
424
425        if send_data {
426            let _ = self
427                .sender
428                .send(WebSocketDomAction::SendMessage(MessageData::Binary(bytes)));
429        }
430        Ok(())
431    }
432
433    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-send>
434    fn Send___(&self, array: CustomAutoRooterGuard<ArrayBufferView>) -> ErrorResult {
435        let bytes = array.to_vec();
436        let data_byte_len = bytes.len();
437        let send_data = self.send_impl(data_byte_len as u64)?;
438
439        if send_data {
440            let _ = self
441                .sender
442                .send(WebSocketDomAction::SendMessage(MessageData::Binary(bytes)));
443        }
444        Ok(())
445    }
446
447    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-close>
448    fn Close(&self, code: Option<u16>, reason: Option<USVString>) -> ErrorResult {
449        if let Some(code) = code {
450            // Fail if the supplied code isn't normal and isn't reserved for libraries, frameworks, and applications
451            if code != close_code::NORMAL && !(3000..=4999).contains(&code) {
452                return Err(Error::InvalidAccess(None));
453            }
454        }
455        if let Some(ref reason) = reason {
456            if reason.0.len() > 123 {
457                // reason cannot be larger than 123 bytes
458                return Err(Error::Syntax(Some("Reason too long".to_string())));
459            }
460        }
461
462        match self.ready_state.get() {
463            WebSocketRequestState::Closing | WebSocketRequestState::Closed => {}, // Do nothing
464            WebSocketRequestState::Connecting => {
465                // Connection is not yet established
466                /*By setting the state to closing, the open function
467                will abort connecting the websocket*/
468                self.ready_state.set(WebSocketRequestState::Closing);
469
470                fail_the_websocket_connection(
471                    Trusted::new(self),
472                    &self
473                        .global()
474                        .task_manager()
475                        .websocket_task_source()
476                        .to_sendable(),
477                );
478            },
479            WebSocketRequestState::Open => {
480                self.ready_state.set(WebSocketRequestState::Closing);
481
482                // Kick off _Start the WebSocket Closing Handshake_
483                // https://tools.ietf.org/html/rfc6455#section-7.1.2
484                let reason = reason.map(|reason| reason.0);
485                let _ = self.sender.send(WebSocketDomAction::Close(code, reason));
486            },
487        }
488        Ok(()) // Return Ok
489    }
490}
491
492struct ReportCSPViolationTask {
493    websocket: Trusted<WebSocket>,
494    violations: Vec<Violation>,
495}
496
497impl TaskOnce for ReportCSPViolationTask {
498    fn run_once(self, _cx: &mut js::context::JSContext) {
499        let global = self.websocket.root().global();
500        global.report_csp_violations(self.violations, None, None);
501    }
502}
503
504/// Task queued when *the WebSocket connection is established*.
505/// <https://html.spec.whatwg.org/multipage/#feedback-from-the-protocol:concept-websocket-established>
506struct ConnectionEstablishedTask {
507    address: Trusted<WebSocket>,
508    protocol_in_use: Option<String>,
509}
510
511impl TaskOnce for ConnectionEstablishedTask {
512    /// <https://html.spec.whatwg.org/multipage/#feedback-from-the-protocol:concept-websocket-established>
513    fn run_once(self, cx: &mut js::context::JSContext) {
514        let ws = self.address.root();
515
516        // Step 1.
517        ws.ready_state.set(WebSocketRequestState::Open);
518
519        // Step 2: Extensions.
520        // TODO: Set extensions to extensions in use.
521
522        // Step 3.
523        if let Some(protocol_name) = self.protocol_in_use {
524            *ws.protocol.borrow_mut() = protocol_name;
525        };
526
527        // Step 4.
528        ws.upcast().fire_event(atom!("open"), CanGc::from_cx(cx));
529    }
530}
531
532struct BufferedAmountTask {
533    address: Trusted<WebSocket>,
534}
535
536impl TaskOnce for BufferedAmountTask {
537    // See https://html.spec.whatwg.org/multipage/#dom-websocket-bufferedamount
538    //
539    // To be compliant with standards, we need to reset bufferedAmount only when the event loop
540    // reaches step 1.  In our implementation, the bytes will already have been sent on a background
541    // thread.
542    fn run_once(self, _cx: &mut js::context::JSContext) {
543        let ws = self.address.root();
544
545        ws.buffered_amount.set(0);
546        ws.clearing_buffer.set(false);
547    }
548}
549
550struct CloseTask {
551    address: Trusted<WebSocket>,
552    failed: bool,
553    code: Option<u16>,
554    reason: Option<String>,
555}
556
557impl TaskOnce for CloseTask {
558    fn run_once(self, cx: &mut js::context::JSContext) {
559        let ws = self.address.root();
560
561        if ws.ready_state.get() == WebSocketRequestState::Closed {
562            // Do nothing if already closed.
563            return;
564        }
565
566        // Perform _the WebSocket connection is closed_ steps.
567        // https://html.spec.whatwg.org/multipage/#closeWebSocket
568
569        // Step 1.
570        ws.ready_state.set(WebSocketRequestState::Closed);
571
572        // Step 2.
573        if self.failed {
574            ws.upcast().fire_event(atom!("error"), CanGc::from_cx(cx));
575        }
576
577        // Step 3.
578        let clean_close = !self.failed;
579        let code = self.code.unwrap_or(close_code::NO_STATUS);
580        let reason = DOMString::from(self.reason.unwrap_or("".to_owned()));
581        let close_event = CloseEvent::new(
582            &ws.global(),
583            atom!("close"),
584            EventBubbles::DoesNotBubble,
585            EventCancelable::NotCancelable,
586            clean_close,
587            code,
588            reason,
589            CanGc::from_cx(cx),
590        );
591        close_event
592            .upcast::<Event>()
593            .fire(ws.upcast(), CanGc::from_cx(cx));
594    }
595}
596
597struct MessageReceivedTask {
598    address: Trusted<WebSocket>,
599    message: MessageData,
600}
601
602impl TaskOnce for MessageReceivedTask {
603    #[expect(unsafe_code)]
604    fn run_once(self, cx: &mut js::context::JSContext) {
605        let ws = self.address.root();
606        debug!(
607            "MessageReceivedTask::handler({:p}): readyState={:?}",
608            &*ws,
609            ws.ready_state.get()
610        );
611
612        // Step 1.
613        if ws.ready_state.get() != WebSocketRequestState::Open {
614            return;
615        }
616
617        // Step 2-5.
618        let global = ws.global();
619        let mut realm = AutoRealm::new(
620            cx,
621            NonNull::new(ws.reflector().get_jsobject().get()).unwrap(),
622        );
623        let cx = &mut *realm;
624        rooted!(&in(cx) let mut message = UndefinedValue());
625        match self.message {
626            MessageData::Text(text) => {
627                text.safe_to_jsval(cx.into(), message.handle_mut(), CanGc::from_cx(cx))
628            },
629            MessageData::Binary(data) => match ws.binary_type.get() {
630                BinaryType::Blob => {
631                    let blob = Blob::new(
632                        &global,
633                        BlobImpl::new_from_bytes(data, "".to_owned()),
634                        CanGc::from_cx(cx),
635                    );
636                    blob.safe_to_jsval(cx.into(), message.handle_mut(), CanGc::from_cx(cx));
637                },
638                BinaryType::Arraybuffer => {
639                    rooted!(&in(cx) let mut array_buffer = ptr::null_mut::<JSObject>());
640                    // GlobalScope::get_cx() returns a valid `JSContext` pointer, so this is safe.
641                    unsafe {
642                        assert!(
643                            ArrayBuffer::create(
644                                cx.raw_cx(),
645                                CreateWith::Slice(&data),
646                                array_buffer.handle_mut()
647                            )
648                            .is_ok()
649                        )
650                    };
651
652                    (*array_buffer).safe_to_jsval(
653                        cx.into(),
654                        message.handle_mut(),
655                        CanGc::from_cx(cx),
656                    );
657                },
658            },
659        }
660        MessageEvent::dispatch_jsval(
661            ws.upcast(),
662            &global,
663            message.handle(),
664            Some(&ws.origin().ascii_serialization()),
665            None,
666            vec![],
667            CanGc::from_cx(cx),
668        );
669    }
670}