1mod blob;
4
5mod broadcast_channel;
6mod compression;
7mod console;
8mod message_port;
9mod stream_resource;
10mod timers;
11mod url;
12mod urlpattern;
13
14use std::borrow::Cow;
15use std::cell::RefCell;
16use std::sync::Arc;
17
18pub use blob::BlobError;
19pub use compression::CompressionError;
20use deno_core::U16String;
21use deno_core::convert::ByteString;
22use deno_core::convert::Uint8Array;
23use deno_core::op2;
24use deno_core::url::Url;
25use deno_core::v8;
26use encoding_rs::CoderResult;
27use encoding_rs::Decoder;
28use encoding_rs::DecoderResult;
29use encoding_rs::Encoding;
30pub use message_port::MessagePortError;
31pub use stream_resource::StreamResourceError;
32
33pub use crate::blob::Blob;
34pub use crate::blob::BlobPart;
35pub use crate::blob::BlobStore;
36pub use crate::blob::InMemoryBlobPart;
37use crate::blob::op_blob_create_object_url;
38use crate::blob::op_blob_create_part;
39use crate::blob::op_blob_from_object_url;
40use crate::blob::op_blob_read_part;
41use crate::blob::op_blob_remove_part;
42use crate::blob::op_blob_revoke_object_url;
43use crate::blob::op_blob_slice_part;
44pub use crate::broadcast_channel::InMemoryBroadcastChannel;
45pub use crate::message_port::JsMessageData;
46pub use crate::message_port::MessagePort;
47pub use crate::message_port::Transferable;
48pub use crate::message_port::create_entangled_message_port;
49pub use crate::message_port::deserialize_js_transferables;
50use crate::message_port::op_message_port_create_entangled;
51use crate::message_port::op_message_port_post_message;
52use crate::message_port::op_message_port_recv_message;
53use crate::message_port::op_message_port_recv_message_sync;
54pub use crate::message_port::serialize_transferables;
55pub use crate::timers::StartTime;
56use crate::timers::op_defer;
57use crate::timers::op_now;
58use crate::timers::op_time_origin;
59
60deno_core::extension!(deno_web,
61 deps = [ deno_webidl ],
62 ops = [
63 op_base64_decode,
64 op_base64_decode_into,
65 op_base64_encode,
66 op_base64_encode_from_buffer,
67 op_base64_atob,
68 op_base64_btoa,
69 op_encoding_normalize_label,
70 op_encoding_decode_single,
71 op_encoding_decode_utf8,
72 op_encoding_new_decoder,
73 op_encoding_decode,
74 op_encoding_encode_into,
75 op_blob_create_part,
76 op_blob_slice_part,
77 op_blob_read_part,
78 op_blob_remove_part,
79 op_blob_create_object_url,
80 op_blob_revoke_object_url,
81 op_blob_from_object_url,
82 op_message_port_create_entangled,
83 op_message_port_post_message,
84 op_message_port_recv_message,
85 op_message_port_recv_message_sync,
86 compression::op_compression_new,
87 compression::op_compression_write,
88 compression::op_compression_finish,
89 op_now,
90 op_time_origin,
91 op_defer,
92 stream_resource::op_readable_stream_resource_allocate,
93 stream_resource::op_readable_stream_resource_allocate_sized,
94 stream_resource::op_readable_stream_resource_get_sink,
95 stream_resource::op_readable_stream_resource_write_error,
96 stream_resource::op_readable_stream_resource_write_buf,
97 stream_resource::op_readable_stream_resource_write_sync,
98 stream_resource::op_readable_stream_resource_close,
99 stream_resource::op_readable_stream_resource_await_close,
100 url::op_url_reparse,
101 url::op_url_parse,
102 url::op_url_get_serialization,
103 url::op_url_parse_with_base,
104 url::op_url_parse_search_params,
105 url::op_url_stringify_search_params,
106 urlpattern::op_urlpattern_parse,
107 urlpattern::op_urlpattern_process_match_input,
108 console::op_preview_entries,
109 broadcast_channel::op_broadcast_subscribe,
110 broadcast_channel::op_broadcast_unsubscribe,
111 broadcast_channel::op_broadcast_send,
112 broadcast_channel::op_broadcast_recv,
113 ],
114 esm = [
115 "00_infra.js",
116 "01_dom_exception.js",
117 "01_mimesniff.js",
118 "02_event.js",
119 "02_structured_clone.js",
120 "02_timers.js",
121 "03_abort_signal.js",
122 "04_global_interfaces.js",
123 "05_base64.js",
124 "06_streams.js",
125 "08_text_encoding.js",
126 "09_file.js",
127 "10_filereader.js",
128 "12_location.js",
129 "13_message_port.js",
130 "14_compression.js",
131 "15_performance.js",
132 "16_image_data.js",
133 "00_url.js",
134 "01_urlpattern.js",
135 "01_console.js",
136 "01_broadcast_channel.js"
137 ],
138 lazy_loaded_esm = [ "webtransport.js" ],
139 options = {
140 blob_store: Arc<BlobStore>,
141 maybe_location: Option<Url>,
142 bc: InMemoryBroadcastChannel,
143 },
144 state = |state, options| {
145 state.put(options.blob_store);
146 if let Some(location) = options.maybe_location {
147 state.put(Location(location));
148 }
149 state.put(StartTime::default());
150 state.put(options.bc);
151 }
152);
153
154#[derive(Debug, thiserror::Error, deno_error::JsError)]
155pub enum WebError {
156 #[class("DOMExceptionInvalidCharacterError")]
157 #[error("Failed to decode base64")]
158 Base64Decode,
159 #[class(range)]
160 #[error("The encoding label provided ('{0}') is invalid.")]
161 InvalidEncodingLabel(String),
162 #[class(type)]
163 #[error("buffer exceeds maximum length")]
164 BufferTooLong,
165 #[class(range)]
166 #[error("Value too large to decode")]
167 ValueTooLarge,
168 #[class(range)]
169 #[error("Provided buffer too small")]
170 BufferTooSmall,
171 #[class(type)]
172 #[error("The encoded data is not valid")]
173 DataInvalid,
174 #[class(generic)]
175 #[error(transparent)]
176 DataError(#[from] v8::DataError),
177}
178
179#[inline]
182fn simdutf_base64_decode_to_vec(input: &[u8]) -> Result<Vec<u8>, WebError> {
183 use v8::simdutf;
184 let max_len = simdutf::maximal_binary_length_from_base64(input);
185 let mut output = Vec::with_capacity(max_len);
186 let result = unsafe {
189 ffi_base64_to_binary(
190 input.as_ptr(),
191 input.len(),
192 output.as_mut_ptr(),
193 simdutf::Base64Options::Default as u64,
194 simdutf::LastChunkHandling::Loose as u64,
195 )
196 };
197 if result.error != 0 {
199 return Err(WebError::Base64Decode);
200 }
201 unsafe { output.set_len(result.count) };
203 Ok(output)
204}
205
206#[inline]
209fn simdutf_base64_decode_into(
210 input: &[u8],
211 output: &mut [u8],
212) -> Result<usize, WebError> {
213 use v8::simdutf;
214 let result = unsafe {
216 simdutf::base64_to_binary(
217 input,
218 output,
219 simdutf::Base64Options::Default,
220 simdutf::LastChunkHandling::Loose,
221 )
222 };
223 if !result.is_ok() {
224 return Err(WebError::Base64Decode);
225 }
226 Ok(result.count)
227}
228
229#[inline]
232fn simdutf_base64_decode_strict(
233 input: &[u8],
234 output: &mut [u8],
235) -> Option<usize> {
236 use v8::simdutf;
237 let result = unsafe {
239 simdutf::base64_to_binary(
240 input,
241 output,
242 simdutf::Base64Options::Default,
243 simdutf::LastChunkHandling::Strict,
244 )
245 };
246 if result.is_ok() {
247 Some(result.count)
248 } else {
249 None
250 }
251}
252
253#[repr(C)]
256struct SimdutfFfiResult {
257 error: i32,
258 count: usize,
259}
260
261unsafe extern "C" {
262 #[link_name = "simdutf__binary_to_base64"]
263 fn ffi_binary_to_base64(
264 input: *const u8,
265 length: usize,
266 output: *mut u8,
267 options: u64,
268 ) -> usize;
269
270 #[link_name = "simdutf__base64_to_binary"]
271 fn ffi_base64_to_binary(
272 input: *const u8,
273 length: usize,
274 output: *mut u8,
275 options: u64,
276 last_chunk_options: u64,
277 ) -> SimdutfFfiResult;
278}
279
280#[inline]
286unsafe fn simdutf_base64_encode(
287 input: &[u8],
288 output: *mut u8,
289 output_len: usize,
290) -> usize {
291 debug_assert!(
292 output_len
293 >= v8::simdutf::base64_length_from_binary(
294 input.len(),
295 v8::simdutf::Base64Options::Default
296 )
297 );
298 unsafe {
300 ffi_binary_to_base64(
301 input.as_ptr(),
302 input.len(),
303 output,
304 v8::simdutf::Base64Options::Default as u64,
305 )
306 }
307}
308
309#[op2]
310fn op_base64_decode(
311 #[string(onebyte)] input: Cow<[u8]>,
312) -> Result<Uint8Array, WebError> {
313 let v = simdutf_base64_decode_to_vec(&input)?;
314 Ok(v.into())
315}
316
317#[op2(fast)]
325fn op_base64_decode_into(
326 #[string(onebyte)] input: Cow<[u8]>,
327 #[buffer] target: &mut [u8],
328 #[smi] offset: u32,
329) -> Result<u32, WebError> {
330 let offset = offset as usize;
331 let target = &mut target[offset..];
332
333 let max_len = v8::simdutf::maximal_binary_length_from_base64(&input);
336 if target.len() >= max_len
337 && let Some(len) = simdutf_base64_decode_strict(&input, target)
338 {
339 return Ok(len as u32);
340 }
341
342 const STACK_BUF_SIZE: usize = 8192;
344 if max_len <= STACK_BUF_SIZE {
345 let mut buf = std::mem::MaybeUninit::<[u8; STACK_BUF_SIZE]>::uninit();
346 let decoded_len = simdutf_base64_decode_into(&input, unsafe {
348 std::slice::from_raw_parts_mut(
349 buf.as_mut_ptr() as *mut u8,
350 STACK_BUF_SIZE,
351 )
352 })?;
353 let bytes_to_write = decoded_len.min(target.len());
354 target[..bytes_to_write].copy_from_slice(unsafe {
356 std::slice::from_raw_parts(buf.as_ptr() as *const u8, bytes_to_write)
357 });
358 Ok(bytes_to_write as u32)
359 } else {
360 let decoded = simdutf_base64_decode_to_vec(&input)?;
361 let bytes_to_write = decoded.len().min(target.len());
362 target[..bytes_to_write].copy_from_slice(&decoded[..bytes_to_write]);
363 Ok(bytes_to_write as u32)
364 }
365}
366
367#[op2]
368fn op_base64_atob(#[scoped] mut s: ByteString) -> Result<ByteString, WebError> {
369 let max_len = v8::simdutf::maximal_binary_length_from_base64(&s);
371 const STACK_BUF_SIZE: usize = 8192;
372 if max_len <= STACK_BUF_SIZE {
373 let mut buf = std::mem::MaybeUninit::<[u8; STACK_BUF_SIZE]>::uninit();
374 let decoded_len = simdutf_base64_decode_into(&s, unsafe {
376 std::slice::from_raw_parts_mut(
377 buf.as_mut_ptr() as *mut u8,
378 STACK_BUF_SIZE,
379 )
380 })?;
381 s[..decoded_len].copy_from_slice(unsafe {
383 std::slice::from_raw_parts(buf.as_ptr() as *const u8, decoded_len)
384 });
385 s.truncate(decoded_len);
386 Ok(s)
387 } else {
388 let decoded = simdutf_base64_decode_to_vec(&s)?;
389 let decoded_len = decoded.len();
390 s[..decoded_len].copy_from_slice(&decoded[..decoded_len]);
391 s.truncate(decoded_len);
392 Ok(s)
393 }
394}
395
396#[op2]
397#[string]
398fn op_base64_encode(#[buffer] s: &[u8]) -> String {
399 forgiving_base64_encode(s)
400}
401
402#[op2]
404fn op_base64_encode_from_buffer<'a>(
405 scope: &mut v8::PinScope<'a, '_>,
406 #[buffer] s: &[u8],
407 #[smi] offset: u32,
408 #[smi] length: u32,
409) -> Result<v8::Local<'a, v8::String>, WebError> {
410 let offset = offset as usize;
411 let length = length as usize;
412 let end = (offset + length).min(s.len());
413 base64_encode_to_v8_string(scope, &s[offset..end])
414}
415
416#[inline]
420fn base64_encode_to_v8_string<'a>(
421 scope: &mut v8::PinScope<'a, '_>,
422 src: &[u8],
423) -> Result<v8::Local<'a, v8::String>, WebError> {
424 let b64_len = v8::simdutf::base64_length_from_binary(
425 src.len(),
426 v8::simdutf::Base64Options::Default,
427 );
428
429 const STACK_BUF_SIZE: usize = 8192;
430 if b64_len <= STACK_BUF_SIZE {
431 let mut buf = std::mem::MaybeUninit::<[u8; STACK_BUF_SIZE]>::uninit();
432 let written = unsafe {
435 simdutf_base64_encode(src, buf.as_mut_ptr() as *mut u8, b64_len)
436 };
437 v8::String::new_from_one_byte(
438 scope,
439 unsafe { std::slice::from_raw_parts(buf.as_ptr() as *const u8, written) },
441 v8::NewStringType::Normal,
442 )
443 .ok_or(WebError::BufferTooLong)
444 } else {
445 let mut buf = Vec::with_capacity(b64_len);
448 let written =
451 unsafe { simdutf_base64_encode(src, buf.as_mut_ptr(), b64_len) };
452 unsafe { buf.set_len(written) };
454 let buf = buf.into_boxed_slice();
455 debug_assert_eq!(written, b64_len);
456 v8::String::new_external_onebyte(scope, buf).ok_or(WebError::BufferTooLong)
457 }
458}
459
460#[op2]
461#[string]
462fn op_base64_btoa(#[scoped] s: ByteString) -> String {
463 forgiving_base64_encode(s.as_ref())
464}
465
466#[inline]
468pub fn forgiving_base64_encode(s: &[u8]) -> String {
469 let b64_len = v8::simdutf::base64_length_from_binary(
470 s.len(),
471 v8::simdutf::Base64Options::Default,
472 );
473 let mut buf = Vec::with_capacity(b64_len);
474 unsafe {
477 let written = simdutf_base64_encode(s, buf.as_mut_ptr(), b64_len);
478 buf.set_len(written);
479 String::from_utf8_unchecked(buf)
480 }
481}
482
483#[op2]
484#[string]
485fn op_encoding_normalize_label(
486 #[string] label: String,
487) -> Result<String, WebError> {
488 let encoding = Encoding::for_label_no_replacement(label.as_bytes())
489 .ok_or(WebError::InvalidEncodingLabel(label))?;
490 Ok(encoding.name().to_lowercase())
491}
492
493#[op2]
494fn op_encoding_decode_utf8<'a>(
495 scope: &mut v8::PinScope<'a, '_>,
496 #[anybuffer] zero_copy: &[u8],
497 ignore_bom: bool,
498) -> Result<v8::Local<'a, v8::String>, WebError> {
499 let buf = &zero_copy;
500
501 let buf = if !ignore_bom
502 && buf.len() >= 3
503 && buf[0] == 0xef
504 && buf[1] == 0xbb
505 && buf[2] == 0xbf
506 {
507 &buf[3..]
508 } else {
509 buf
510 };
511
512 match v8::String::new_from_utf8(scope, buf, v8::NewStringType::Normal) {
521 Some(text) => Ok(text),
522 None => Err(WebError::BufferTooLong),
523 }
524}
525
526#[op2]
527#[serde]
528fn op_encoding_decode_single(
529 #[anybuffer] data: &[u8],
530 #[string] label: String,
531 fatal: bool,
532 ignore_bom: bool,
533) -> Result<U16String, WebError> {
534 let encoding = Encoding::for_label(label.as_bytes())
535 .ok_or(WebError::InvalidEncodingLabel(label))?;
536
537 let mut decoder = if ignore_bom {
538 encoding.new_decoder_without_bom_handling()
539 } else {
540 encoding.new_decoder_with_bom_removal()
541 };
542
543 let max_buffer_length = decoder
544 .max_utf16_buffer_length(data.len())
545 .ok_or(WebError::ValueTooLarge)?;
546
547 let mut output = vec![0; max_buffer_length];
548
549 if fatal {
550 let (result, _, written) =
551 decoder.decode_to_utf16_without_replacement(data, &mut output, true);
552 match result {
553 DecoderResult::InputEmpty => {
554 output.truncate(written);
555 Ok(output.into())
556 }
557 DecoderResult::OutputFull => Err(WebError::BufferTooSmall),
558 DecoderResult::Malformed(_, _) => Err(WebError::DataInvalid),
559 }
560 } else {
561 let (result, _, written, _) =
562 decoder.decode_to_utf16(data, &mut output, true);
563 match result {
564 CoderResult::InputEmpty => {
565 output.truncate(written);
566 Ok(output.into())
567 }
568 CoderResult::OutputFull => Err(WebError::BufferTooSmall),
569 }
570 }
571}
572
573#[op2]
574#[cppgc]
575fn op_encoding_new_decoder(
576 #[string] label: &str,
577 fatal: bool,
578 ignore_bom: bool,
579) -> Result<TextDecoderResource, WebError> {
580 let encoding = Encoding::for_label(label.as_bytes())
581 .ok_or_else(|| WebError::InvalidEncodingLabel(label.to_string()))?;
582
583 let decoder = if ignore_bom {
584 encoding.new_decoder_without_bom_handling()
585 } else {
586 encoding.new_decoder_with_bom_removal()
587 };
588
589 Ok(TextDecoderResource {
590 decoder: RefCell::new(decoder),
591 fatal,
592 })
593}
594
595#[op2]
596#[serde]
597fn op_encoding_decode(
598 #[anybuffer] data: &[u8],
599 #[cppgc] resource: &TextDecoderResource,
600 stream: bool,
601) -> Result<U16String, WebError> {
602 let mut decoder = resource.decoder.borrow_mut();
603 let fatal = resource.fatal;
604
605 let max_buffer_length = decoder
606 .max_utf16_buffer_length(data.len())
607 .ok_or(WebError::ValueTooLarge)?;
608
609 let mut output = vec![0; max_buffer_length];
610
611 if fatal {
612 let (result, _, written) =
613 decoder.decode_to_utf16_without_replacement(data, &mut output, !stream);
614 match result {
615 DecoderResult::InputEmpty => {
616 output.truncate(written);
617 Ok(output.into())
618 }
619 DecoderResult::OutputFull => Err(WebError::BufferTooSmall),
620 DecoderResult::Malformed(_, _) => Err(WebError::DataInvalid),
621 }
622 } else {
623 let (result, _, written, _) =
624 decoder.decode_to_utf16(data, &mut output, !stream);
625 match result {
626 CoderResult::InputEmpty => {
627 output.truncate(written);
628 Ok(output.into())
629 }
630 CoderResult::OutputFull => Err(WebError::BufferTooSmall),
631 }
632 }
633}
634
635struct TextDecoderResource {
636 decoder: RefCell<Decoder>,
637 fatal: bool,
638}
639
640unsafe impl deno_core::GarbageCollected for TextDecoderResource {
642 fn trace(&self, _visitor: &mut deno_core::v8::cppgc::Visitor) {}
643
644 fn get_name(&self) -> &'static std::ffi::CStr {
645 c"TextDecoderResource"
646 }
647}
648
649#[op2(fast(op_encoding_encode_into_fast))]
650fn op_encoding_encode_into(
651 scope: &mut v8::PinScope<'_, '_>,
652 input: v8::Local<v8::Value>,
653 #[buffer] buffer: &mut [u8],
654 #[buffer] out_buf: &mut [u32],
655) -> Result<(), WebError> {
656 let s = v8::Local::<v8::String>::try_from(input)?;
657
658 let mut nchars = 0;
659 let len = s.write_utf8_v2(
660 scope,
661 buffer,
662 v8::WriteFlags::kReplaceInvalidUtf8,
663 Some(&mut nchars),
664 );
665 out_buf[1] = len as u32;
666 out_buf[0] = nchars as u32;
667 Ok(())
668}
669
670#[op2(fast)]
671fn op_encoding_encode_into_fast(
672 #[string] input: Cow<'_, str>,
673 #[buffer] buffer: &mut [u8],
674 #[buffer] out_buf: &mut [u32],
675) {
676 let boundary = if buffer.len() >= input.len() {
680 input.len()
681 } else {
682 let mut boundary = buffer.len();
683
684 for _ in 0..4 {
686 if input.is_char_boundary(boundary) {
687 break;
688 }
689 debug_assert!(boundary > 0);
690 boundary -= 1;
691 }
692
693 debug_assert!(input.is_char_boundary(boundary));
694 boundary
695 };
696
697 buffer[..boundary].copy_from_slice(input[..boundary].as_bytes());
698
699 out_buf[0] = match input {
701 Cow::Borrowed(v) => v[..boundary].len() as u32,
704 Cow::Owned(v) => v[..boundary].encode_utf16().count() as u32,
705 };
706 out_buf[1] = boundary as u32;
707}
708
709pub struct Location(pub Url);