Skip to main content

script/dom/document/
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::cell::Cell;
6use std::ptr::{self, NonNull};
7use std::rc::Rc;
8
9use dom_struct::dom_struct;
10use js::context::JSContext;
11use js::conversions::ToJSValConvertible;
12use js::jsapi::JSObject;
13use js::jsval::UndefinedValue;
14use js::realm::AutoRealm;
15use js::rust::{CustomAutoRooterGuard, HandleObject};
16use js::typedarray::{ArrayBuffer, ArrayBufferU8, ArrayBufferView};
17use net_traits::blob_url_store::UrlWithBlobClaim;
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::generic_callback::GenericCallback as ProfileGenericCallback;
26use script_bindings::cell::DomRefCell;
27use script_bindings::reflector::{DomObject, reflect_weak_referenceable_dom_object_with_proto};
28use servo_base::generic_channel::{LazyCallback, lazy_callback};
29use servo_constellation_traits::BlobImpl;
30use servo_url::{ImmutableOrigin, ServoUrl};
31
32use crate::dom::bindings::buffer_source::create_buffer_source;
33use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
34use crate::dom::bindings::codegen::Bindings::WebSocketBinding::{BinaryType, WebSocketMethods};
35use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
36use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence;
37use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
38use crate::dom::bindings::inheritance::Castable;
39use crate::dom::bindings::refcounted::Trusted;
40use crate::dom::bindings::reflector::DomGlobal;
41use crate::dom::bindings::root::DomRoot;
42use crate::dom::bindings::str::{DOMString, USVString, is_token};
43use crate::dom::blob::Blob;
44use crate::dom::closeevent::CloseEvent;
45use crate::dom::csp::{GlobalCspReporting, Violation};
46use crate::dom::event::{Event, EventBubbles, EventCancelable};
47use crate::dom::eventtarget::EventTarget;
48use crate::dom::globalscope::GlobalScope;
49use crate::dom::messageevent::MessageEvent;
50use crate::dom::window::Window;
51use crate::fetch::fetch::RequestWithGlobalScope;
52use crate::tasks::task::TaskOnce;
53use crate::tasks::task_source::SendableTaskSource;
54
55#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
56enum WebSocketRequestState {
57    Connecting = 0,
58    Open = 1,
59    Closing = 2,
60    Closed = 3,
61}
62
63// Close codes defined in https://tools.ietf.org/html/rfc6455#section-7.4.1
64// Names are from https://github.com/mozilla/gecko-dev/blob/master/netwerk/protocol/websocket/nsIWebSocketChannel.idl
65#[expect(dead_code)]
66mod close_code {
67    pub(crate) const NORMAL: u16 = 1000;
68    pub(crate) const GOING_AWAY: u16 = 1001;
69    pub(crate) const PROTOCOL_ERROR: u16 = 1002;
70    pub(crate) const UNSUPPORTED_DATATYPE: u16 = 1003;
71    pub(crate) const NO_STATUS: u16 = 1005;
72    pub(crate) const ABNORMAL: u16 = 1006;
73    pub(crate) const INVALID_PAYLOAD: u16 = 1007;
74    pub(crate) const POLICY_VIOLATION: u16 = 1008;
75    pub(crate) const TOO_LARGE: u16 = 1009;
76    pub(crate) const EXTENSION_MISSING: u16 = 1010;
77    pub(crate) const INTERNAL_ERROR: u16 = 1011;
78    pub(crate) const TLS_FAILED: u16 = 1015;
79}
80
81fn close_the_websocket_connection(
82    address: Trusted<WebSocket>,
83    task_source: &SendableTaskSource,
84    code: Option<u16>,
85    reason: String,
86) {
87    task_source.queue(CloseTask {
88        address,
89        failed: false,
90        code,
91        reason: Some(reason),
92    });
93}
94
95fn fail_the_websocket_connection(address: Trusted<WebSocket>, task_source: &SendableTaskSource) {
96    task_source.queue(CloseTask {
97        address,
98        failed: true,
99        code: Some(close_code::ABNORMAL),
100        reason: None,
101    });
102}
103
104#[dom_struct]
105pub(crate) struct WebSocket {
106    eventtarget: EventTarget,
107    #[no_trace]
108    url: ServoUrl,
109    ready_state: Cell<WebSocketRequestState>,
110    buffered_amount: Cell<u64>,
111    clearing_buffer: Cell<bool>, // Flag to tell if there is a running thread to clear buffered_amount
112    #[no_trace]
113    callback: LazyCallback<WebSocketDomAction>,
114    binary_type: Cell<BinaryType>,
115    protocol: DomRefCell<String>, // Subprotocol selected by server
116}
117
118impl WebSocket {
119    fn new_inherited(url: ServoUrl, callback: LazyCallback<WebSocketDomAction>) -> WebSocket {
120        WebSocket {
121            eventtarget: EventTarget::new_inherited(),
122            url,
123            ready_state: Cell::new(WebSocketRequestState::Connecting),
124            buffered_amount: Cell::new(0),
125            clearing_buffer: Cell::new(false),
126            callback,
127            binary_type: Cell::new(BinaryType::Blob),
128            protocol: Default::default(),
129        }
130    }
131
132    fn new(
133        cx: &mut JSContext,
134        global: &GlobalScope,
135        proto: Option<HandleObject>,
136        url: ServoUrl,
137        sender: LazyCallback<WebSocketDomAction>,
138    ) -> DomRoot<WebSocket> {
139        let websocket = reflect_weak_referenceable_dom_object_with_proto(
140            cx,
141            Rc::new(WebSocket::new_inherited(url, sender)),
142            global,
143            proto,
144        );
145        if let Some(window) = global.downcast::<Window>() {
146            window.Document().track_websocket(&websocket);
147        }
148        websocket
149    }
150
151    /// <https://websockets.spec.whatwg.org/#dom-websocket-send>
152    fn send_impl(&self, data_byte_len: u64) -> Fallible<bool> {
153        let return_after_buffer = match self.ready_state.get() {
154            WebSocketRequestState::Connecting => {
155                return Err(Error::InvalidState(Some(
156                    "Cannot send while socket is connecting".into(),
157                )));
158            },
159            WebSocketRequestState::Open => false,
160            WebSocketRequestState::Closing | WebSocketRequestState::Closed => true,
161        };
162
163        let address = Trusted::new(self);
164
165        match data_byte_len.checked_add(self.buffered_amount.get()) {
166            None => panic!(),
167            Some(new_amount) => self.buffered_amount.set(new_amount),
168        };
169
170        if return_after_buffer {
171            return Ok(false);
172        }
173
174        if !self.clearing_buffer.get() && self.ready_state.get() == WebSocketRequestState::Open {
175            self.clearing_buffer.set(true);
176
177            // TODO(mrobinson): Should this task be cancellable?
178            self.global()
179                .task_manager()
180                .websocket_task_source()
181                .queue_unconditionally(BufferedAmountTask { address });
182        }
183
184        Ok(true)
185    }
186
187    pub(crate) fn origin(&self) -> ImmutableOrigin {
188        self.url.origin()
189    }
190
191    /// <https://websockets.spec.whatwg.org/#make-disappear>
192    /// Returns true if any action was taken.
193    pub(crate) fn make_disappear(&self) -> bool {
194        let result = self.ready_state.get() != WebSocketRequestState::Closed;
195        let _ = self.Close(Some(1001), None);
196        result
197    }
198}
199
200impl WebSocketMethods<crate::DomTypeHolder> for WebSocket {
201    /// <https://websockets.spec.whatwg.org/#dom-websocket-websocket>
202    fn Constructor(
203        cx: &mut JSContext,
204        global: &GlobalScope,
205        proto: Option<HandleObject>,
206        url: DOMString,
207        protocols: Option<StringOrStringSequence>,
208    ) -> Fallible<DomRoot<WebSocket>> {
209        // Step 1. Let baseURL be this's relevant settings object's API base URL.
210        let base_url = global.api_base_url();
211        // Step 2. Let urlRecord be the result of applying the URL parser to url with baseURL.
212        // Step 3. If urlRecord is failure, then throw a "SyntaxError" DOMException.
213        let mut url_record = ServoUrl::parse_with_base(Some(&base_url), &url.str())
214            .or(Err(Error::Syntax(Some("Failed to parse url".into()))))?;
215
216        // Step 4. If urlRecord’s scheme is "http", then set urlRecord’s scheme to "ws".
217        // Step 5. Otherwise, if urlRecord’s scheme is "https", set urlRecord’s scheme to "wss".
218        // Step 6. If urlRecord’s scheme is not "ws" or "wss", then throw a "SyntaxError" DOMException.
219        match url_record.scheme() {
220            "http" => {
221                url_record
222                    .as_mut_url()
223                    .set_scheme("ws")
224                    .expect("Can't set scheme from http to ws");
225            },
226            "https" => {
227                url_record
228                    .as_mut_url()
229                    .set_scheme("wss")
230                    .expect("Can't set scheme from https to wss");
231            },
232            "ws" | "wss" => {},
233            _ => {
234                return Err(Error::Syntax(Some("Forbidden URL scheme".into())));
235            },
236        }
237
238        // Step 7. If urlRecord’s fragment is non-null, then throw a "SyntaxError" DOMException.
239        if url_record.fragment().is_some() {
240            return Err(Error::Syntax(Some("Forbidden URL fragment".into())));
241        }
242
243        // Step 8. If protocols is a string, set protocols to a sequence consisting of just that string.
244        let protocols = protocols.map_or(vec![], |p| match p {
245            StringOrStringSequence::String(string) => vec![string.into()],
246            StringOrStringSequence::StringSequence(seq) => {
247                seq.into_iter().map(String::from).collect()
248            },
249        });
250
251        // Step 9. If any of the values in protocols occur more than once or otherwise fail to match the requirements
252        // for elements that comprise the value of `Sec-WebSocket-Protocol` fields as defined by The WebSocket protocol,
253        // then throw a "SyntaxError" DOMException.
254        for (i, protocol) in protocols.iter().enumerate() {
255            // https://tools.ietf.org/html/rfc6455#section-4.1
256            // Handshake requirements, step 10
257
258            if protocols[i + 1..]
259                .iter()
260                .any(|p| p.eq_ignore_ascii_case(protocol))
261            {
262                return Err(Error::Syntax(Some("Duplicate protocol header".into())));
263            }
264
265            // https://tools.ietf.org/html/rfc6455#section-4.1
266            if !is_token(protocol.as_bytes()) {
267                return Err(Error::Syntax(Some(
268                    "Protocol header field is not a valid token".into(),
269                )));
270            }
271        }
272
273        // Create the interface for communication with the resource thread
274        let (dom_action_sender, resource_action_receiver) = lazy_callback();
275
276        // Step 12. Establish a WebSocket connection given urlRecord, protocols, and client.
277        let ws = WebSocket::new(cx, global, proto, url_record.clone(), dom_action_sender);
278        let address = Trusted::new(&*ws);
279
280        // https://websockets.spec.whatwg.org/#concept-websocket-establish
281        //
282        // Let request be a new request, whose URL is requestURL, client is client, service-workers
283        // mode is "none", referrer is "no-referrer", mode is "websocket", credentials mode is
284        // "include", cache mode is "no-store" , and redirect mode is "error"
285        let request = RequestBuilder::new(
286            global.webview_id(),
287            UrlWithBlobClaim::from_url_without_having_claimed_blob(url_record.clone()),
288            Referrer::NoReferrer,
289        )
290        .with_global_scope(global)
291        .mode(RequestMode::WebSocket {
292            protocols,
293            original_url: url_record,
294        })
295        .service_workers_mode(ServiceWorkersMode::None)
296        .credentials_mode(CredentialsMode::Include)
297        .cache_mode(CacheMode::NoCache)
298        .redirect_mode(RedirectMode::Error);
299
300        let task_source = global.task_manager().websocket_task_source().to_sendable();
301        let resource_event_sender =
302            ProfileGenericCallback::new(move |message| match message.unwrap() {
303                WebSocketNetworkEvent::ReportCSPViolations(violations) => {
304                    let task = ReportCSPViolationTask {
305                        websocket: address.clone(),
306                        violations,
307                    };
308                    task_source.queue(task);
309                },
310                WebSocketNetworkEvent::ConnectionEstablished { protocol_in_use } => {
311                    let open_thread = ConnectionEstablishedTask {
312                        address: address.clone(),
313                        protocol_in_use,
314                    };
315                    task_source.queue(open_thread);
316                },
317                WebSocketNetworkEvent::MessageReceived(message) => {
318                    let message_thread = MessageReceivedTask {
319                        address: address.clone(),
320                        message,
321                    };
322                    task_source.queue(message_thread);
323                },
324                WebSocketNetworkEvent::Fail => {
325                    fail_the_websocket_connection(address.clone(), &task_source);
326                },
327                WebSocketNetworkEvent::Close(code, reason) => {
328                    close_the_websocket_connection(address.clone(), &task_source, code, reason);
329                },
330            })
331            .expect("Couldn't create web socket callback.");
332
333        let channels = FetchChannels::WebSocket {
334            event_sender: resource_event_sender,
335            action_receiver: resource_action_receiver,
336        };
337        let _ = global
338            .core_resource_thread()
339            .send(CoreResourceMsg::Fetch(request, channels));
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                .callback
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                .callback
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().unwrap_or_default();
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                .callback
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().unwrap_or_default();
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                .callback
442                .send(WebSocketDomAction::SendMessage(MessageData::Binary(bytes)));
443        }
444        Ok(())
445    }
446
447    /// <https://websockets.spec.whatwg.org/#dom-websocket-close>
448    fn Close(&self, code: Option<u16>, reason: Option<USVString>) -> ErrorResult {
449        // Step 1. If code is present, but is neither an integer equal to 1000 nor an integer in the range 3000 to 4999, inclusive, throw an "InvalidAccessError" DOMException.
450        if let Some(code) = code &&
451            code != close_code::NORMAL &&
452            !(3000..=4999).contains(&code)
453        {
454            return Err(Error::InvalidAccess(Some(
455                "Invalid WebSocket connection close code".into(),
456            )));
457        }
458
459        // Step 2.2. If reasonBytes is longer than 123 bytes, then throw a "SyntaxError" DOMException.
460        if let Some(ref reason) = reason &&
461            reason.0.len() > 123
462        {
463            return Err(Error::Syntax(Some("Reason too long".to_string())));
464        }
465
466        // Step 3. Run the first matching steps from the following list:
467        match self.ready_state.get() {
468            WebSocketRequestState::Closing | WebSocketRequestState::Closed => {}, // Do nothing
469            WebSocketRequestState::Connecting => {
470                // If the WebSocket connection is not yet established [WSP] Fail the WebSocket connection and set this’s ready state to CLOSING (2).
471                self.ready_state.set(WebSocketRequestState::Closing);
472
473                fail_the_websocket_connection(
474                    Trusted::new(self),
475                    &self
476                        .global()
477                        .task_manager()
478                        .websocket_task_source()
479                        .to_sendable(),
480                );
481            },
482            WebSocketRequestState::Open => {
483                // If the WebSocket closing handshake has not yet been started [WSP] Start the WebSocket closing handshake and set this’s ready state to CLOSING (2). [WSP]
484                self.ready_state.set(WebSocketRequestState::Closing);
485
486                // Kick off _Start the WebSocket Closing Handshake_
487                // https://tools.ietf.org/html/rfc6455#section-7.1.2
488                let reason = reason.map(|reason| reason.0);
489                let _ = self.callback.send(WebSocketDomAction::Close(code, reason));
490            },
491        }
492        Ok(()) // Return Ok
493    }
494}
495
496struct ReportCSPViolationTask {
497    websocket: Trusted<WebSocket>,
498    violations: Vec<Violation>,
499}
500
501impl TaskOnce for ReportCSPViolationTask {
502    fn run_once(self, cx: &mut JSContext) {
503        let global = self.websocket.root().global();
504        global.report_csp_violations(cx, self.violations, None, None);
505    }
506}
507
508/// Task queued when *the WebSocket connection is established*.
509/// <https://html.spec.whatwg.org/multipage/#feedback-from-the-protocol:concept-websocket-established>
510struct ConnectionEstablishedTask {
511    address: Trusted<WebSocket>,
512    protocol_in_use: Option<String>,
513}
514
515impl TaskOnce for ConnectionEstablishedTask {
516    /// <https://html.spec.whatwg.org/multipage/#feedback-from-the-protocol:concept-websocket-established>
517    fn run_once(self, cx: &mut JSContext) {
518        let ws = self.address.root();
519
520        // Step 1.
521        ws.ready_state.set(WebSocketRequestState::Open);
522
523        // Step 2: Extensions.
524        // TODO: Set extensions to extensions in use.
525
526        // Step 3.
527        if let Some(protocol_name) = self.protocol_in_use {
528            *ws.protocol.borrow_mut() = protocol_name;
529        };
530
531        // Step 4.
532        ws.upcast().fire_event(cx, atom!("open"));
533    }
534}
535
536struct BufferedAmountTask {
537    address: Trusted<WebSocket>,
538}
539
540impl TaskOnce for BufferedAmountTask {
541    // See https://html.spec.whatwg.org/multipage/#dom-websocket-bufferedamount
542    //
543    // To be compliant with standards, we need to reset bufferedAmount only when the event loop
544    // reaches step 1.  In our implementation, the bytes will already have been sent on a background
545    // thread.
546    fn run_once(self, _cx: &mut JSContext) {
547        let ws = self.address.root();
548
549        ws.buffered_amount.set(0);
550        ws.clearing_buffer.set(false);
551    }
552}
553
554struct CloseTask {
555    address: Trusted<WebSocket>,
556    failed: bool,
557    code: Option<u16>,
558    reason: Option<String>,
559}
560
561impl TaskOnce for CloseTask {
562    fn run_once(self, cx: &mut JSContext) {
563        let ws = self.address.root();
564
565        if ws.ready_state.get() == WebSocketRequestState::Closed {
566            // Do nothing if already closed.
567            return;
568        }
569
570        // Perform _the WebSocket connection is closed_ steps.
571        // https://html.spec.whatwg.org/multipage/#closeWebSocket
572
573        // Step 1.
574        ws.ready_state.set(WebSocketRequestState::Closed);
575
576        // Step 2.
577        if self.failed {
578            ws.upcast().fire_event(cx, atom!("error"));
579        }
580
581        // Step 3.
582        let clean_close = !self.failed;
583        let code = self.code.unwrap_or(close_code::NO_STATUS);
584        let reason = DOMString::from(self.reason.unwrap_or_default());
585        let close_event = CloseEvent::new(
586            cx,
587            &ws.global(),
588            atom!("close"),
589            EventBubbles::DoesNotBubble,
590            EventCancelable::NotCancelable,
591            clean_close,
592            code,
593            reason,
594        );
595        close_event.upcast::<Event>().fire(cx, ws.upcast());
596    }
597}
598
599struct MessageReceivedTask {
600    address: Trusted<WebSocket>,
601    message: MessageData,
602}
603
604impl TaskOnce for MessageReceivedTask {
605    fn run_once(self, cx: &mut JSContext) {
606        let ws = self.address.root();
607        debug!(
608            "MessageReceivedTask::handler({:p}): readyState={:?}",
609            &*ws,
610            ws.ready_state.get()
611        );
612
613        // Step 1.
614        if ws.ready_state.get() != WebSocketRequestState::Open {
615            return;
616        }
617
618        // Step 2-5.
619        let global = ws.global();
620        let mut realm = AutoRealm::new(
621            cx,
622            NonNull::new(ws.reflector().get_jsobject().get()).unwrap(),
623        );
624        let cx = &mut *realm;
625        rooted!(&in(cx) let mut message = UndefinedValue());
626        match self.message {
627            MessageData::Text(text) => text.to_jsval(cx, message.handle_mut()),
628            MessageData::Binary(data) => match ws.binary_type.get() {
629                BinaryType::Blob => {
630                    let blob =
631                        Blob::new(cx, &global, BlobImpl::new_from_bytes(data, String::new()));
632                    blob.to_jsval(cx, message.handle_mut());
633                },
634                BinaryType::Arraybuffer => {
635                    rooted!(&in(cx) let mut array_buffer = ptr::null_mut::<JSObject>());
636                    assert!(
637                        create_buffer_source::<ArrayBufferU8>(cx, &data, array_buffer.handle_mut())
638                            .is_ok()
639                    );
640
641                    (*array_buffer).to_jsval(cx, message.handle_mut());
642                },
643            },
644        }
645        MessageEvent::dispatch_jsval(
646            cx,
647            ws.upcast(),
648            &global,
649            message.handle(),
650            Some(ws.origin().ascii_serialization().as_ref()),
651            None,
652            vec![],
653        );
654    }
655}