Skip to main content

script/dom/webgl/
webgl2renderingcontext.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;
6#[cfg(feature = "webxr")]
7use std::rc::Rc;
8use std::{cmp, ptr};
9
10use bitflags::bitflags;
11use dom_struct::dom_struct;
12use euclid::default::{Point2D, Rect, Size2D};
13use js::context::JSContext;
14use js::jsapi::{JSObject, Type};
15use js::jsval::{BooleanValue, DoubleValue, Int32Value, NullValue, ObjectValue, UInt32Value};
16use js::rust::{CustomAutoRooterGuard, HandleObject, MutableHandleObject, MutableHandleValue};
17use js::typedarray::{ArrayBufferView, CreateWith, Float32, Int32Array, Uint32, Uint32Array};
18use pixels::{Alpha, Snapshot};
19use script_bindings::conversions::SafeToJSValConvertible;
20use script_bindings::interfaces::WebGL2RenderingContextHelpers;
21use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
22use servo_base::generic_channel::{self, GenericSharedMemory};
23use servo_canvas_traits::webgl::WebGLError::*;
24use servo_canvas_traits::webgl::{
25    AlphaTreatment, GLContextAttributes, InternalFormatParameter, TexDataType, TexFormat,
26    WebGLCommand, WebGLContextId, WebGLResult, WebGLVersion, YAxisTreatment, webgl_channel,
27};
28use servo_config::pref;
29use url::Host;
30use webrender_api::ImageKey;
31
32use super::validations::types::TexImageTarget;
33use crate::canvas_context::CanvasContext;
34use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::{
35    WebGL2RenderingContextConstants as constants, WebGL2RenderingContextMethods,
36};
37use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{
38    TexImageSource, WebGLContextAttributes, WebGLRenderingContextMethods,
39};
40use crate::dom::bindings::codegen::UnionTypes::{
41    ArrayBufferViewOrArrayBuffer, Float32ArrayOrUnrestrictedFloatSequence,
42    HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas,
43    Int32ArrayOrLongSequence, Uint32ArrayOrUnsignedLongSequence,
44};
45use crate::dom::bindings::error::{ErrorResult, Fallible};
46use crate::dom::bindings::reflector::DomGlobal;
47use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
48use crate::dom::bindings::str::DOMString;
49use crate::dom::globalscope::GlobalScope;
50#[cfg(feature = "webxr")]
51use crate::dom::promise::Promise;
52use crate::dom::webgl::validations::WebGLValidator;
53use crate::dom::webgl::validations::tex_image_2d::{
54    TexImage2DValidator, TexImage2DValidatorResult, TexStorageValidator, TexStorageValidatorResult,
55};
56use crate::dom::webgl::validations::tex_image_3d::{
57    TexImage3DValidator, TexImage3DValidatorResult,
58};
59use crate::dom::webgl::webglactiveinfo::WebGLActiveInfo;
60use crate::dom::webgl::webglbuffer::WebGLBuffer;
61use crate::dom::webgl::webglframebuffer::{WebGLFramebuffer, WebGLFramebufferAttachmentRoot};
62use crate::dom::webgl::webglprogram::WebGLProgram;
63use crate::dom::webgl::webglquery::WebGLQuery;
64use crate::dom::webgl::webglrenderbuffer::WebGLRenderbuffer;
65use crate::dom::webgl::webglrenderingcontext::{
66    Operation, TexPixels, TexSource, VertexAttrib, WebGLRenderingContext, uniform_get,
67    uniform_typed,
68};
69use crate::dom::webgl::webglsampler::{WebGLSampler, WebGLSamplerValue};
70use crate::dom::webgl::webglshader::WebGLShader;
71use crate::dom::webgl::webglshaderprecisionformat::WebGLShaderPrecisionFormat;
72use crate::dom::webgl::webglsync::WebGLSync;
73use crate::dom::webgl::webgltexture::WebGLTexture;
74use crate::dom::webgl::webgltransformfeedback::WebGLTransformFeedback;
75use crate::dom::webgl::webgluniformlocation::WebGLUniformLocation;
76use crate::dom::webgl::webglvertexarrayobject::WebGLVertexArrayObject;
77use crate::dom::window::Window;
78
79#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
80#[derive(JSTraceable, MallocSizeOf)]
81struct IndexedBinding {
82    buffer: MutNullableDom<WebGLBuffer>,
83    start: Cell<i64>,
84    size: Cell<i64>,
85}
86
87impl IndexedBinding {
88    fn new() -> IndexedBinding {
89        IndexedBinding {
90            buffer: MutNullableDom::new(None),
91            start: Cell::new(0),
92            size: Cell::new(0),
93        }
94    }
95}
96
97#[dom_struct] // no need to report size here as it is reported as part of WebGLRenderingContext
98pub(crate) struct WebGL2RenderingContext {
99    reflector_: Reflector,
100    base: Dom<WebGLRenderingContext>,
101    occlusion_query: MutNullableDom<WebGLQuery>,
102    primitives_query: MutNullableDom<WebGLQuery>,
103    samplers: Box<[MutNullableDom<WebGLSampler>]>,
104    bound_copy_read_buffer: MutNullableDom<WebGLBuffer>,
105    bound_copy_write_buffer: MutNullableDom<WebGLBuffer>,
106    bound_pixel_pack_buffer: MutNullableDom<WebGLBuffer>,
107    bound_pixel_unpack_buffer: MutNullableDom<WebGLBuffer>,
108    bound_transform_feedback_buffer: MutNullableDom<WebGLBuffer>,
109    bound_uniform_buffer: MutNullableDom<WebGLBuffer>,
110    indexed_uniform_buffer_bindings: Box<[IndexedBinding]>,
111    indexed_transform_feedback_buffer_bindings: Box<[IndexedBinding]>,
112    current_transform_feedback: MutNullableDom<WebGLTransformFeedback>,
113    texture_pack_row_length: Cell<usize>,
114    texture_pack_skip_pixels: Cell<usize>,
115    texture_pack_skip_rows: Cell<usize>,
116    enable_rasterizer_discard: Cell<bool>,
117    default_fb_readbuffer: Cell<u32>,
118    default_fb_drawbuffer: Cell<u32>,
119}
120
121struct ReadPixelsAllowedFormats<'a> {
122    array_types: &'a [Type],
123    channels: usize,
124}
125
126struct ReadPixelsSizes {
127    row_stride: usize,
128    skipped_bytes: usize,
129    size: usize,
130}
131
132impl WebGL2RenderingContext {
133    fn new_inherited(
134        cx: &mut JSContext,
135        window: &Window,
136        canvas: &RootedHTMLCanvasElementOrOffscreenCanvas,
137        size: Size2D<u32>,
138        attrs: GLContextAttributes,
139    ) -> Option<WebGL2RenderingContext> {
140        let base =
141            WebGLRenderingContext::new(cx, window, canvas, WebGLVersion::WebGL2, size, attrs)?;
142
143        let samplers = (0..base.limits().max_combined_texture_image_units)
144            .map(|_| Default::default())
145            .collect::<Vec<_>>()
146            .into();
147        let indexed_uniform_buffer_bindings = (0..base.limits().max_uniform_buffer_bindings)
148            .map(|_| IndexedBinding::new())
149            .collect::<Vec<_>>()
150            .into();
151        let indexed_transform_feedback_buffer_bindings =
152            (0..base.limits().max_transform_feedback_separate_attribs)
153                .map(|_| IndexedBinding::new())
154                .collect::<Vec<_>>()
155                .into();
156
157        Some(WebGL2RenderingContext {
158            reflector_: Reflector::new(),
159            base: Dom::from_ref(&*base),
160            occlusion_query: MutNullableDom::new(None),
161            primitives_query: MutNullableDom::new(None),
162            samplers,
163            bound_copy_read_buffer: MutNullableDom::new(None),
164            bound_copy_write_buffer: MutNullableDom::new(None),
165            bound_pixel_pack_buffer: MutNullableDom::new(None),
166            bound_pixel_unpack_buffer: MutNullableDom::new(None),
167            bound_transform_feedback_buffer: MutNullableDom::new(None),
168            bound_uniform_buffer: MutNullableDom::new(None),
169            indexed_uniform_buffer_bindings,
170            indexed_transform_feedback_buffer_bindings,
171            current_transform_feedback: MutNullableDom::new(None),
172            texture_pack_row_length: Cell::new(0),
173            texture_pack_skip_pixels: Cell::new(0),
174            texture_pack_skip_rows: Cell::new(0),
175            enable_rasterizer_discard: Cell::new(false),
176            default_fb_readbuffer: Cell::new(constants::BACK),
177            default_fb_drawbuffer: Cell::new(constants::BACK),
178        })
179    }
180
181    pub(crate) fn new(
182        cx: &mut js::context::JSContext,
183        window: &Window,
184        canvas: &RootedHTMLCanvasElementOrOffscreenCanvas,
185        size: Size2D<u32>,
186        attrs: GLContextAttributes,
187    ) -> Option<DomRoot<WebGL2RenderingContext>> {
188        WebGL2RenderingContext::new_inherited(cx, window, canvas, size, attrs)
189            .map(|ctx| reflect_dom_object_with_cx(Box::new(ctx), window, cx))
190    }
191
192    pub(crate) fn set_image_key(&self, image_key: ImageKey) {
193        self.base.set_image_key(image_key);
194    }
195
196    #[expect(unsafe_code)]
197    pub(crate) fn is_webgl2_enabled(
198        _cx: &mut js::context::JSContext,
199        global: HandleObject,
200    ) -> bool {
201        if pref!(dom_webgl2_enabled) {
202            return true;
203        }
204
205        let global = unsafe { GlobalScope::from_object(global.get()) };
206        let origin = global.origin();
207        let host = origin.host();
208        WEBGL2_ORIGINS
209            .iter()
210            .any(|origin| host == Host::parse(origin).ok().as_ref())
211    }
212}
213
214/// List of domains for which WebGL 2 is enabled automatically, regardless
215/// of the status of the dom.webgl2.enabled preference.
216static WEBGL2_ORIGINS: &[&str] = &["www.servoexperiments.com"];
217
218impl WebGL2RenderingContext {
219    pub(crate) fn current_vao(&self, cx: &mut JSContext) -> DomRoot<WebGLVertexArrayObject> {
220        self.base.current_vao_webgl2(cx)
221    }
222
223    pub(crate) fn validate_uniform_block_for_draw(&self) {
224        let program = match self.base.current_program() {
225            Some(program) => program,
226            None => return,
227        };
228        for uniform_block in program.active_uniform_blocks().iter() {
229            let data_size = uniform_block.size as usize;
230            for block in program.active_uniforms().iter() {
231                let index = match block.bind_index {
232                    Some(index) => index,
233                    None => continue,
234                };
235                let indexed = &self.indexed_uniform_buffer_bindings[index as usize];
236                let buffer = match indexed.buffer.get() {
237                    Some(buffer) => buffer,
238                    None => {
239                        self.base.webgl_error(InvalidOperation);
240                        return;
241                    },
242                };
243                if indexed.size.get() == 0 {
244                    if data_size > buffer.capacity() {
245                        self.base.webgl_error(InvalidOperation);
246                        return;
247                    }
248                } else {
249                    let start = indexed.start.get() as usize;
250                    let mut size = indexed.size.get() as usize;
251                    if start >= size {
252                        self.base.webgl_error(InvalidOperation);
253                        return;
254                    }
255                    size -= start;
256                    if data_size > size {
257                        self.base.webgl_error(InvalidOperation);
258                        return;
259                    }
260                }
261            }
262        }
263    }
264
265    fn validate_vertex_attribs_for_draw(&self, cx: &mut JSContext) {
266        let program = match self.base.current_program() {
267            Some(program) => program,
268            None => return,
269        };
270        let groups = [
271            [
272                constants::INT,
273                constants::INT_VEC2,
274                constants::INT_VEC3,
275                constants::INT_VEC4,
276            ],
277            [
278                constants::UNSIGNED_INT,
279                constants::UNSIGNED_INT_VEC2,
280                constants::UNSIGNED_INT_VEC3,
281                constants::UNSIGNED_INT_VEC4,
282            ],
283            [
284                constants::FLOAT,
285                constants::FLOAT_VEC2,
286                constants::FLOAT_VEC3,
287                constants::FLOAT_VEC4,
288            ],
289        ];
290        let vao = self.current_vao(cx);
291        for prog_attrib in program.active_attribs().iter() {
292            let attrib = handle_potential_webgl_error!(
293                self.base,
294                // TODO(#34300): remove unwrap
295                vao.get_vertex_attrib(prog_attrib.location.unwrap_or(u32::MAX))
296                    .ok_or(InvalidOperation),
297                return
298            );
299
300            // TODO(#34300): remove unwrap
301            let current_vertex_attrib = self.base.current_vertex_attribs()[prog_attrib
302                .location
303                .map(|l| l as usize)
304                .unwrap_or(usize::MAX)];
305            let attrib_data_base_type = if !attrib.enabled_as_array {
306                match current_vertex_attrib {
307                    VertexAttrib::Int(_, _, _, _) => constants::INT,
308                    VertexAttrib::Uint(_, _, _, _) => constants::UNSIGNED_INT,
309                    VertexAttrib::Float(_, _, _, _) => constants::FLOAT,
310                }
311            } else {
312                attrib.type_
313            };
314
315            let contains = groups
316                .iter()
317                .find(|g| g.contains(&attrib_data_base_type) && g.contains(&prog_attrib.type_));
318            if contains.is_none() {
319                self.base.webgl_error(InvalidOperation);
320                return;
321            }
322        }
323    }
324
325    pub(crate) fn base_context(&self) -> DomRoot<WebGLRenderingContext> {
326        DomRoot::from_ref(&*self.base)
327    }
328
329    fn bound_buffer(
330        &self,
331        cx: &mut JSContext,
332        target: u32,
333    ) -> WebGLResult<Option<DomRoot<WebGLBuffer>>> {
334        match target {
335            constants::COPY_READ_BUFFER => Ok(self.bound_copy_read_buffer.get()),
336            constants::COPY_WRITE_BUFFER => Ok(self.bound_copy_write_buffer.get()),
337            constants::PIXEL_PACK_BUFFER => Ok(self.bound_pixel_pack_buffer.get()),
338            constants::PIXEL_UNPACK_BUFFER => Ok(self.bound_pixel_unpack_buffer.get()),
339            constants::TRANSFORM_FEEDBACK_BUFFER => Ok(self.bound_transform_feedback_buffer.get()),
340            constants::UNIFORM_BUFFER => Ok(self.bound_uniform_buffer.get()),
341            constants::ELEMENT_ARRAY_BUFFER => {
342                Ok(self.current_vao(cx).element_array_buffer().get())
343            },
344            _ => self.base.bound_buffer(cx, target),
345        }
346    }
347
348    pub(crate) fn buffer_usage(&self, usage: u32) -> WebGLResult<u32> {
349        match usage {
350            constants::STATIC_READ |
351            constants::DYNAMIC_READ |
352            constants::STREAM_READ |
353            constants::STATIC_COPY |
354            constants::DYNAMIC_COPY |
355            constants::STREAM_COPY => Ok(usage),
356            _ => self.base.buffer_usage(usage),
357        }
358    }
359
360    fn unbind_from(&self, slot: &MutNullableDom<WebGLBuffer>, buffer: &WebGLBuffer) {
361        if slot.get().is_some_and(|b| buffer == &*b) {
362            buffer.decrement_attached_counter(Operation::Infallible);
363            slot.set(None);
364        }
365    }
366
367    fn calc_read_pixel_formats(
368        &self,
369        pixel_type: u32,
370        format: u32,
371    ) -> WebGLResult<ReadPixelsAllowedFormats<'_>> {
372        let array_types = match pixel_type {
373            constants::BYTE => &[Type::Int8][..],
374            constants::SHORT => &[Type::Int16][..],
375            constants::INT => &[Type::Int32][..],
376            constants::UNSIGNED_BYTE => &[Type::Uint8, Type::Uint8Clamped][..],
377            constants::UNSIGNED_SHORT |
378            constants::UNSIGNED_SHORT_4_4_4_4 |
379            constants::UNSIGNED_SHORT_5_5_5_1 |
380            constants::UNSIGNED_SHORT_5_6_5 => &[Type::Uint16][..],
381            constants::UNSIGNED_INT |
382            constants::UNSIGNED_INT_2_10_10_10_REV |
383            constants::UNSIGNED_INT_10F_11F_11F_REV |
384            constants::UNSIGNED_INT_5_9_9_9_REV => &[Type::Uint32][..],
385            constants::FLOAT => &[Type::Float32][..],
386            constants::HALF_FLOAT => &[Type::Uint16][..],
387            _ => return Err(InvalidEnum),
388        };
389        let channels = match format {
390            constants::ALPHA | constants::RED | constants::RED_INTEGER => 1,
391            constants::RG | constants::RG_INTEGER => 2,
392            constants::RGB | constants::RGB_INTEGER => 3,
393            constants::RGBA | constants::RGBA_INTEGER => 4,
394            _ => return Err(InvalidEnum),
395        };
396        Ok(ReadPixelsAllowedFormats {
397            array_types,
398            channels,
399        })
400    }
401
402    fn calc_read_pixel_sizes(
403        &self,
404        width: i32,
405        height: i32,
406        bytes_per_pixel: usize,
407    ) -> WebGLResult<ReadPixelsSizes> {
408        if width < 0 || height < 0 {
409            return Err(InvalidValue);
410        }
411
412        // See also https://www.khronos.org/registry/webgl/specs/latest/2.0/#6.36
413        let pixels_per_row = if self.texture_pack_row_length.get() > 0 {
414            self.texture_pack_row_length.get()
415        } else {
416            width as usize
417        };
418        if self.texture_pack_skip_pixels.get() + width as usize > pixels_per_row {
419            return Err(InvalidOperation);
420        }
421
422        let bytes_per_row = pixels_per_row
423            .checked_mul(bytes_per_pixel)
424            .ok_or(InvalidOperation)?;
425        let row_padding_bytes = {
426            let pack_alignment = self.base.get_texture_packing_alignment() as usize;
427            match bytes_per_row % pack_alignment {
428                0 => 0,
429                remainder => pack_alignment - remainder,
430            }
431        };
432        let row_stride = bytes_per_row + row_padding_bytes;
433        let size = if width == 0 || height == 0 {
434            0
435        } else {
436            let full_row_bytes = row_stride
437                .checked_mul(height as usize - 1)
438                .ok_or(InvalidOperation)?;
439            let last_row_bytes = bytes_per_pixel
440                .checked_mul(width as usize)
441                .ok_or(InvalidOperation)?;
442            full_row_bytes
443                .checked_add(last_row_bytes)
444                .ok_or(InvalidOperation)?
445        };
446        let skipped_bytes = {
447            let skipped_row_bytes = self
448                .texture_pack_skip_rows
449                .get()
450                .checked_mul(row_stride)
451                .ok_or(InvalidOperation)?;
452            let skipped_pixel_bytes = self
453                .texture_pack_skip_pixels
454                .get()
455                .checked_mul(bytes_per_pixel)
456                .ok_or(InvalidOperation)?;
457            skipped_row_bytes
458                .checked_add(skipped_pixel_bytes)
459                .ok_or(InvalidOperation)?
460        };
461        Ok(ReadPixelsSizes {
462            row_stride,
463            skipped_bytes,
464            size,
465        })
466    }
467
468    #[expect(unsafe_code)]
469    #[expect(clippy::too_many_arguments)]
470    fn read_pixels_into(
471        &self,
472        x: i32,
473        y: i32,
474        width: i32,
475        height: i32,
476        format: u32,
477        pixel_type: u32,
478        dst: &mut ArrayBufferView,
479        dst_elem_offset: u32,
480    ) {
481        handle_potential_webgl_error!(self.base, self.base.validate_framebuffer(), return);
482
483        if self.bound_pixel_pack_buffer.get().is_some() {
484            return self.base.webgl_error(InvalidOperation);
485        }
486
487        let fb_slot = self.base.get_draw_framebuffer_slot();
488        let fb_readbuffer_valid = match fb_slot.get() {
489            Some(fb) => fb.attachment(fb.read_buffer()).is_some(),
490            None => self.default_fb_readbuffer.get() != constants::NONE,
491        };
492        if !fb_readbuffer_valid {
493            return self.base.webgl_error(InvalidOperation);
494        }
495
496        let dst_byte_offset = {
497            let dst_elem_size = dst.get_array_type().byte_size().unwrap();
498            dst_elem_offset as usize * dst_elem_size
499        };
500        if dst_byte_offset > dst.len() {
501            return self.base.webgl_error(InvalidValue);
502        }
503
504        let dst_array_type = dst.get_array_type();
505        let ReadPixelsAllowedFormats {
506            array_types: allowed_array_types,
507            channels,
508        } = match self.calc_read_pixel_formats(pixel_type, format) {
509            Ok(result) => result,
510            Err(error) => return self.base.webgl_error(error),
511        };
512        if !allowed_array_types.contains(&dst_array_type) {
513            return self.base.webgl_error(InvalidOperation);
514        }
515        if format != constants::RGBA || pixel_type != constants::UNSIGNED_BYTE {
516            return self.base.webgl_error(InvalidOperation);
517        }
518
519        let bytes_per_pixel = dst_array_type.byte_size().unwrap() * channels;
520        let ReadPixelsSizes {
521            row_stride,
522            skipped_bytes,
523            size,
524        } = match self.calc_read_pixel_sizes(width, height, bytes_per_pixel) {
525            Ok(result) => result,
526            Err(error) => return self.base.webgl_error(error),
527        };
528        let dst_end = dst_byte_offset + skipped_bytes + size;
529        let dst_pixels = unsafe { dst.as_mut_slice() };
530        if dst_pixels.len() < dst_end {
531            return self.base.webgl_error(InvalidOperation);
532        }
533
534        let dst_byte_offset = {
535            let margin_left = cmp::max(0, -x) as usize;
536            let margin_top = cmp::max(0, -y) as usize;
537            dst_byte_offset +
538                skipped_bytes +
539                margin_left * bytes_per_pixel +
540                margin_top * row_stride
541        };
542        let src_rect = {
543            let (fb_width, fb_height) = handle_potential_webgl_error!(
544                self.base,
545                self.base
546                    .get_current_framebuffer_size()
547                    .ok_or(InvalidOperation),
548                return
549            );
550            let src_origin = Point2D::new(x, y);
551            let src_size = Size2D::new(width as u32, height as u32);
552            let fb_size = Size2D::new(fb_width as u32, fb_height as u32);
553            match pixels::clip(src_origin, src_size.to_u32(), fb_size.to_u32()) {
554                Some(rect) => rect.to_u32(),
555                None => return,
556            }
557        };
558        let src_row_bytes = handle_potential_webgl_error!(
559            self.base,
560            src_rect
561                .size
562                .width
563                .checked_mul(bytes_per_pixel as u32)
564                .ok_or(InvalidOperation),
565            return
566        );
567
568        let (sender, receiver) = generic_channel::channel().unwrap();
569        self.base.send_command(WebGLCommand::ReadPixels(
570            src_rect, format, pixel_type, sender,
571        ));
572        let (src, _) = receiver.recv().unwrap();
573
574        for i in 0..src_rect.size.height as usize {
575            let src_start = i * src_row_bytes as usize;
576            let dst_start = dst_byte_offset + i * row_stride;
577            dst_pixels[dst_start..dst_start + src_row_bytes as usize]
578                .copy_from_slice(&src[src_start..src_start + src_row_bytes as usize]);
579        }
580    }
581
582    fn uniform_vec_section_uint(
583        &self,
584        vec: Uint32ArrayOrUnsignedLongSequence,
585        offset: u32,
586        length: u32,
587        uniform_size: usize,
588        uniform_location: &WebGLUniformLocation,
589    ) -> WebGLResult<Vec<u32>> {
590        let vec = match vec {
591            Uint32ArrayOrUnsignedLongSequence::Uint32Array(v) => v.to_vec(),
592            Uint32ArrayOrUnsignedLongSequence::UnsignedLongSequence(v) => v,
593        };
594        self.base
595            .uniform_vec_section::<u32>(vec, offset, length, uniform_size, uniform_location)
596    }
597
598    fn get_default_fb_attachment_param(
599        &self,
600        attachment: u32,
601        pname: u32,
602        mut retval: MutableHandleValue,
603    ) -> WebGLResult<()> {
604        match attachment {
605            constants::BACK | constants::DEPTH | constants::STENCIL => {},
606            _ => return Err(InvalidEnum),
607        }
608
609        if pname == constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME {
610            retval.set(NullValue());
611            return Ok(());
612        }
613
614        let attrs = self
615            .GetContextAttributes()
616            .unwrap_or_else(WebGLContextAttributes::empty);
617
618        let intval = match pname {
619            constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE => match attachment {
620                constants::DEPTH if !attrs.depth => constants::NONE as _,
621                constants::STENCIL if !attrs.stencil => constants::NONE as _,
622                _ => constants::FRAMEBUFFER_DEFAULT as _,
623            },
624            constants::FRAMEBUFFER_ATTACHMENT_RED_SIZE |
625            constants::FRAMEBUFFER_ATTACHMENT_GREEN_SIZE |
626            constants::FRAMEBUFFER_ATTACHMENT_BLUE_SIZE => match attachment {
627                constants::BACK => 8,
628                _ => 0,
629            },
630            constants::FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE => match attachment {
631                constants::BACK if attrs.alpha => 8,
632                constants::BACK => return Err(InvalidOperation),
633                _ => 0,
634            },
635            constants::FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE => match attachment {
636                constants::DEPTH if attrs.depth => 24,
637                constants::DEPTH => return Err(InvalidOperation),
638                _ => 0,
639            },
640            constants::FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE => match attachment {
641                constants::STENCIL if attrs.stencil => 8,
642                constants::STENCIL => return Err(InvalidOperation),
643                _ => 0,
644            },
645            constants::FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE => match attachment {
646                constants::DEPTH if attrs.depth => constants::UNSIGNED_NORMALIZED as _,
647                constants::STENCIL if attrs.stencil => constants::UNSIGNED_INT as _,
648                constants::DEPTH => return Err(InvalidOperation),
649                constants::STENCIL => return Err(InvalidOperation),
650                _ => constants::UNSIGNED_NORMALIZED as _,
651            },
652            constants::FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING => match attachment {
653                constants::DEPTH if !attrs.depth => return Err(InvalidOperation),
654                constants::STENCIL if !attrs.stencil => return Err(InvalidOperation),
655                _ => constants::LINEAR as _,
656            },
657            _ => return Err(InvalidEnum),
658        };
659        retval.set(Int32Value(intval));
660        Ok(())
661    }
662
663    fn get_specific_fb_attachment_param(
664        &self,
665        cx: &mut JSContext,
666        fb: &WebGLFramebuffer,
667        target: u32,
668        attachment: u32,
669        pname: u32,
670        mut rval: MutableHandleValue,
671    ) -> WebGLResult<()> {
672        use crate::dom::webgl::webglframebuffer::WebGLFramebufferAttachmentRoot::{
673            Renderbuffer, Texture,
674        };
675
676        match attachment {
677            constants::DEPTH_ATTACHMENT | constants::STENCIL_ATTACHMENT => {},
678            constants::DEPTH_STENCIL_ATTACHMENT => {
679                if pname == constants::FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE {
680                    return Err(InvalidOperation);
681                }
682
683                let a = fb.attachment(constants::DEPTH_ATTACHMENT);
684                let b = fb.attachment(constants::STENCIL_ATTACHMENT);
685                match (a, b) {
686                    (Some(Renderbuffer(ref a)), Some(Renderbuffer(ref b))) if a.id() == b.id() => {
687                    },
688                    (Some(Texture(ref a)), Some(Texture(ref b))) if a.id() == b.id() => {},
689                    _ => return Err(InvalidOperation),
690                }
691            },
692            constants::COLOR_ATTACHMENT0..=constants::COLOR_ATTACHMENT15 => {
693                let last_slot =
694                    constants::COLOR_ATTACHMENT0 + self.base.limits().max_color_attachments - 1;
695                if last_slot < attachment {
696                    return Err(InvalidEnum);
697                }
698            },
699            _ => return Err(InvalidEnum),
700        }
701
702        let attachment = match attachment {
703            constants::DEPTH_STENCIL_ATTACHMENT => constants::DEPTH_ATTACHMENT,
704            _ => attachment,
705        };
706
707        if pname == constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME {
708            match fb.attachment(attachment) {
709                Some(Renderbuffer(rb)) => {
710                    rb.safe_to_jsval(cx, rval);
711                },
712                Some(Texture(texture)) => {
713                    texture.safe_to_jsval(cx, rval);
714                },
715                _ => rval.set(NullValue()),
716            }
717            return Ok(());
718        }
719
720        match pname {
721            constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE => {},
722            _ => match fb.attachment(attachment) {
723                Some(webgl_attachment) => match pname {
724                    constants::FRAMEBUFFER_ATTACHMENT_RED_SIZE |
725                    constants::FRAMEBUFFER_ATTACHMENT_GREEN_SIZE |
726                    constants::FRAMEBUFFER_ATTACHMENT_BLUE_SIZE |
727                    constants::FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE |
728                    constants::FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE |
729                    constants::FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE |
730                    constants::FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE |
731                    constants::FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING => {},
732                    _ => match webgl_attachment {
733                        WebGLFramebufferAttachmentRoot::Renderbuffer(_) => return Err(InvalidEnum),
734                        WebGLFramebufferAttachmentRoot::Texture(_) => match pname {
735                            constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL |
736                            constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE |
737                            constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER => {},
738                            _ => return Err(InvalidEnum),
739                        },
740                    },
741                },
742                None => return Err(InvalidOperation),
743            },
744        }
745
746        let (sender, receiver) = webgl_channel().unwrap();
747        self.base
748            .send_command(WebGLCommand::GetFramebufferAttachmentParameter(
749                target, attachment, pname, sender,
750            ));
751
752        let retval = receiver.recv().unwrap();
753        rval.set(Int32Value(retval));
754        Ok(())
755    }
756
757    fn clearbuffer_array_size(&self, buffer: u32, draw_buffer: i32) -> WebGLResult<usize> {
758        match buffer {
759            constants::COLOR => {
760                if draw_buffer < 0 || draw_buffer as u32 >= self.base.limits().max_draw_buffers {
761                    return Err(InvalidValue);
762                }
763                Ok(4)
764            },
765            constants::DEPTH | constants::STENCIL | constants::DEPTH_STENCIL => {
766                if draw_buffer != 0 {
767                    return Err(InvalidValue);
768                }
769                Ok(1)
770            },
771            _ => unreachable!(),
772        }
773    }
774
775    fn clear_buffer<T: Clone>(
776        &self,
777        buffer: u32,
778        draw_buffer: i32,
779        valid_buffers: &[u32],
780        src_offset: u32,
781        array: Vec<T>,
782        msg: fn(u32, i32, Vec<T>) -> WebGLCommand,
783    ) {
784        if !valid_buffers.contains(&buffer) {
785            return self.base.webgl_error(InvalidEnum);
786        }
787
788        let array_size = handle_potential_webgl_error!(
789            self.base,
790            self.clearbuffer_array_size(buffer, draw_buffer),
791            return
792        );
793        let src_offset = src_offset as usize;
794
795        if array.len() < src_offset + array_size {
796            return self.base.webgl_error(InvalidValue);
797        }
798        let array = array[src_offset..src_offset + array_size].to_vec();
799
800        self.base.send_command(msg(buffer, draw_buffer, array));
801    }
802
803    fn valid_fb_attachment_values(&self, target: u32, attachments: &[u32]) -> bool {
804        let fb_slot = match target {
805            constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
806                self.base.get_draw_framebuffer_slot()
807            },
808            constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
809            _ => {
810                self.base.webgl_error(InvalidEnum);
811                return false;
812            },
813        };
814
815        if let Some(fb) = fb_slot.get() {
816            if fb.check_status() != constants::FRAMEBUFFER_COMPLETE {
817                return false;
818            }
819
820            for &attachment in attachments {
821                match attachment {
822                    constants::DEPTH_ATTACHMENT |
823                    constants::STENCIL_ATTACHMENT |
824                    constants::DEPTH_STENCIL_ATTACHMENT => {},
825                    constants::COLOR_ATTACHMENT0..=constants::COLOR_ATTACHMENT15 => {
826                        let last_slot = constants::COLOR_ATTACHMENT0 +
827                            self.base.limits().max_color_attachments -
828                            1;
829                        if last_slot < attachment {
830                            return false;
831                        }
832                    },
833                    _ => return false,
834                }
835            }
836        } else {
837            for &attachment in attachments {
838                match attachment {
839                    constants::COLOR | constants::DEPTH | constants::STENCIL => {},
840                    _ => return false,
841                }
842            }
843        }
844
845        true
846    }
847
848    fn vertex_attrib_i(&self, cx: &mut JSContext, index: u32, x: i32, y: i32, z: i32, w: i32) {
849        if index >= self.base.limits().max_vertex_attribs {
850            return self.base.webgl_error(InvalidValue);
851        }
852        self.base.current_vertex_attribs()[index as usize] = VertexAttrib::Int(x, y, z, w);
853        self.current_vao(cx)
854            .set_vertex_attrib_type(index, constants::INT);
855        self.base
856            .send_command(WebGLCommand::VertexAttribI(index, x, y, z, w));
857    }
858
859    fn vertex_attrib_u(&self, cx: &mut JSContext, index: u32, x: u32, y: u32, z: u32, w: u32) {
860        if index >= self.base.limits().max_vertex_attribs {
861            return self.base.webgl_error(InvalidValue);
862        }
863        self.base.current_vertex_attribs()[index as usize] = VertexAttrib::Uint(x, y, z, w);
864        self.current_vao(cx)
865            .set_vertex_attrib_type(index, constants::UNSIGNED_INT);
866        self.base
867            .send_command(WebGLCommand::VertexAttribU(index, x, y, z, w));
868    }
869
870    #[expect(clippy::too_many_arguments)]
871    fn tex_storage(
872        &self,
873        dimensions: u8,
874        target: u32,
875        levels: i32,
876        internal_format: u32,
877        width: i32,
878        height: i32,
879        depth: i32,
880    ) {
881        let expected_dimensions = match target {
882            constants::TEXTURE_2D | constants::TEXTURE_CUBE_MAP => 2,
883            constants::TEXTURE_3D | constants::TEXTURE_2D_ARRAY => 3,
884            _ => return self.base.webgl_error(InvalidEnum),
885        };
886        if dimensions != expected_dimensions {
887            return self.base.webgl_error(InvalidEnum);
888        }
889
890        let validator = TexStorageValidator::new(
891            &self.base,
892            dimensions,
893            target,
894            levels,
895            internal_format,
896            width,
897            height,
898            depth,
899        );
900        let TexStorageValidatorResult {
901            texture,
902            target,
903            levels,
904            internal_format,
905            width,
906            height,
907            depth,
908        } = match validator.validate() {
909            Ok(result) => result,
910            Err(_) => return, // NB: The validator sets the correct error for us.
911        };
912
913        handle_potential_webgl_error!(
914            self.base,
915            texture.storage(target, levels, internal_format, width, height, depth)
916        );
917    }
918
919    #[expect(clippy::too_many_arguments)]
920    fn tex_image_3d(
921        &self,
922        texture: &WebGLTexture,
923        target: TexImageTarget,
924        data_type: TexDataType,
925        internal_format: TexFormat,
926        format: TexFormat,
927        level: u32,
928        width: u32,
929        height: u32,
930        depth: u32,
931        _border: u32,
932        unpacking_alignment: u32,
933        data: TexPixels,
934    ) {
935        handle_potential_webgl_error!(
936            self.base,
937            texture.initialize(
938                target,
939                width,
940                height,
941                depth,
942                internal_format,
943                level,
944                Some(data_type)
945            )
946        );
947
948        let internal_format = self
949            .base
950            .extension_manager()
951            .get_effective_tex_internal_format(internal_format, data_type.as_gl_constant());
952        let effective_data_type = self
953            .base
954            .extension_manager()
955            .effective_type(data_type.as_gl_constant());
956
957        self.base.send_command(WebGLCommand::TexImage3D {
958            target: target.as_gl_constant(),
959            level,
960            internal_format,
961            size: data.size(),
962            depth,
963            format,
964            data_type,
965            effective_data_type,
966            unpacking_alignment,
967            alpha_treatment: data.alpha_treatment(),
968            y_axis_treatment: data.y_axis_treatment(),
969            pixel_format: data.pixel_format(),
970            data: data.into_shared_memory().into(),
971        });
972        // TODO: Hint/tex_parameter
973
974        if let Some(fb) = self.base.bound_draw_framebuffer() {
975            fb.invalidate_texture(texture);
976        }
977    }
978}
979
980impl CanvasContext for WebGL2RenderingContext {
981    type ID = WebGLContextId;
982
983    fn context_id(&self) -> Self::ID {
984        self.base.context_id()
985    }
986
987    fn canvas(&self) -> Option<RootedHTMLCanvasElementOrOffscreenCanvas> {
988        self.base.canvas()
989    }
990
991    fn resize(&self) {
992        self.base.resize();
993    }
994
995    fn reset_bitmap(&self) {
996        self.base.reset_bitmap();
997    }
998
999    fn get_image_data(&self) -> Option<Snapshot> {
1000        self.base.get_image_data()
1001    }
1002
1003    fn mark_as_dirty(&self) {
1004        self.base.mark_as_dirty()
1005    }
1006}
1007
1008impl WebGL2RenderingContextMethods<crate::DomTypeHolder> for WebGL2RenderingContext {
1009    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1>
1010    fn Canvas(&self) -> RootedHTMLCanvasElementOrOffscreenCanvas {
1011        self.base.Canvas()
1012    }
1013
1014    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11>
1015    fn Flush(&self) {
1016        self.base.Flush()
1017    }
1018
1019    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11>
1020    fn Finish(&self) {
1021        self.base.Finish()
1022    }
1023
1024    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1>
1025    fn DrawingBufferWidth(&self) -> i32 {
1026        self.base.DrawingBufferWidth()
1027    }
1028
1029    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1>
1030    fn DrawingBufferHeight(&self) -> i32 {
1031        self.base.DrawingBufferHeight()
1032    }
1033
1034    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
1035    fn GetBufferParameter(
1036        &self,
1037        cx: &mut JSContext,
1038        target: u32,
1039        parameter: u32,
1040        mut retval: MutableHandleValue,
1041    ) {
1042        let buffer = handle_potential_webgl_error!(
1043            self.base,
1044            self.bound_buffer(cx, target),
1045            return retval.set(NullValue())
1046        );
1047        self.base.get_buffer_param(buffer, parameter, retval)
1048    }
1049
1050    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1051    fn GetParameter(&self, cx: &mut JSContext, parameter: u32, mut rval: MutableHandleValue) {
1052        match parameter {
1053            constants::VERSION => {
1054                "WebGL 2.0".safe_to_jsval(cx, rval);
1055                return;
1056            },
1057            constants::SHADING_LANGUAGE_VERSION => {
1058                "WebGL GLSL ES 3.00".safe_to_jsval(cx, rval);
1059                return;
1060            },
1061            constants::MAX_CLIENT_WAIT_TIMEOUT_WEBGL => {
1062                rval.set(DoubleValue(
1063                    self.base.limits().max_client_wait_timeout_webgl.as_nanos() as f64,
1064                ));
1065                return;
1066            },
1067            constants::MAX_SERVER_WAIT_TIMEOUT => {
1068                rval.set(DoubleValue(
1069                    self.base.limits().max_server_wait_timeout.as_nanos() as f64,
1070                ));
1071                return;
1072            },
1073            constants::SAMPLER_BINDING => {
1074                let idx = (self.base.textures().active_unit_enum() - constants::TEXTURE0) as usize;
1075                assert!(idx < self.samplers.len());
1076                let sampler = self.samplers[idx].get();
1077                sampler.safe_to_jsval(cx, rval);
1078                return;
1079            },
1080            constants::COPY_READ_BUFFER_BINDING => {
1081                self.bound_copy_read_buffer.get().safe_to_jsval(cx, rval);
1082                return;
1083            },
1084            constants::COPY_WRITE_BUFFER_BINDING => {
1085                self.bound_copy_write_buffer.get().safe_to_jsval(cx, rval);
1086                return;
1087            },
1088            constants::PIXEL_PACK_BUFFER_BINDING => {
1089                self.bound_pixel_pack_buffer.get().safe_to_jsval(cx, rval);
1090                return;
1091            },
1092            constants::PIXEL_UNPACK_BUFFER_BINDING => {
1093                self.bound_pixel_unpack_buffer.get().safe_to_jsval(cx, rval);
1094                return;
1095            },
1096            constants::TRANSFORM_FEEDBACK_BUFFER_BINDING => {
1097                self.bound_transform_feedback_buffer
1098                    .get()
1099                    .safe_to_jsval(cx, rval);
1100                return;
1101            },
1102            constants::UNIFORM_BUFFER_BINDING => {
1103                self.bound_uniform_buffer.get().safe_to_jsval(cx, rval);
1104                return;
1105            },
1106            constants::TRANSFORM_FEEDBACK_BINDING => {
1107                self.current_transform_feedback
1108                    .get()
1109                    .safe_to_jsval(cx, rval);
1110                return;
1111            },
1112            constants::ELEMENT_ARRAY_BUFFER_BINDING => {
1113                let buffer = self.current_vao(cx).element_array_buffer().get();
1114                buffer.safe_to_jsval(cx, rval);
1115                return;
1116            },
1117            constants::VERTEX_ARRAY_BINDING => {
1118                let vao = self.current_vao(cx);
1119                let vao = vao.id().map(|_| &*vao);
1120                vao.safe_to_jsval(cx, rval);
1121                return;
1122            },
1123            // NOTE: DRAW_FRAMEBUFFER_BINDING is the same as FRAMEBUFFER_BINDING, handled on the WebGL1 side
1124            constants::READ_FRAMEBUFFER_BINDING => {
1125                self.base
1126                    .get_read_framebuffer_slot()
1127                    .get()
1128                    .safe_to_jsval(cx, rval);
1129                return;
1130            },
1131            constants::READ_BUFFER => {
1132                let buffer = match self.base.get_read_framebuffer_slot().get() {
1133                    Some(fb) => fb.read_buffer(),
1134                    None => self.default_fb_readbuffer.get(),
1135                };
1136                rval.set(UInt32Value(buffer));
1137                return;
1138            },
1139            constants::DRAW_BUFFER0..=constants::DRAW_BUFFER15 => {
1140                let buffer = match self.base.get_read_framebuffer_slot().get() {
1141                    Some(fb) => {
1142                        let idx = parameter - constants::DRAW_BUFFER0;
1143                        fb.draw_buffer_i(idx as usize)
1144                    },
1145                    None if parameter == constants::DRAW_BUFFER0 => {
1146                        self.default_fb_readbuffer.get()
1147                    },
1148                    None => constants::NONE,
1149                };
1150                rval.set(UInt32Value(buffer));
1151                return;
1152            },
1153            constants::MAX_TEXTURE_LOD_BIAS => {
1154                rval.set(DoubleValue(self.base.limits().max_texture_lod_bias as f64));
1155                return;
1156            },
1157            constants::MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS => {
1158                rval.set(DoubleValue(
1159                    self.base.limits().max_combined_fragment_uniform_components as f64,
1160                ));
1161                return;
1162            },
1163            constants::MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS => {
1164                rval.set(DoubleValue(
1165                    self.base.limits().max_combined_vertex_uniform_components as f64,
1166                ));
1167                return;
1168            },
1169            constants::MAX_ELEMENT_INDEX => {
1170                rval.set(DoubleValue(self.base.limits().max_element_index as f64));
1171                return;
1172            },
1173            constants::MAX_UNIFORM_BLOCK_SIZE => {
1174                rval.set(DoubleValue(
1175                    self.base.limits().max_uniform_block_size as f64,
1176                ));
1177                return;
1178            },
1179            constants::MIN_PROGRAM_TEXEL_OFFSET => {
1180                rval.set(Int32Value(self.base.limits().min_program_texel_offset));
1181                return;
1182            },
1183            _ => {},
1184        }
1185
1186        let limit = match parameter {
1187            constants::MAX_3D_TEXTURE_SIZE => Some(self.base.limits().max_3d_texture_size),
1188            constants::MAX_ARRAY_TEXTURE_LAYERS => {
1189                Some(self.base.limits().max_array_texture_layers)
1190            },
1191            constants::MAX_COLOR_ATTACHMENTS => Some(self.base.limits().max_color_attachments),
1192            constants::MAX_COMBINED_UNIFORM_BLOCKS => {
1193                Some(self.base.limits().max_combined_uniform_blocks)
1194            },
1195            constants::MAX_DRAW_BUFFERS => Some(self.base.limits().max_draw_buffers),
1196            constants::MAX_ELEMENTS_INDICES => Some(self.base.limits().max_elements_indices),
1197            constants::MAX_ELEMENTS_VERTICES => Some(self.base.limits().max_elements_vertices),
1198            constants::MAX_FRAGMENT_INPUT_COMPONENTS => {
1199                Some(self.base.limits().max_fragment_input_components)
1200            },
1201            constants::MAX_FRAGMENT_UNIFORM_BLOCKS => {
1202                Some(self.base.limits().max_fragment_uniform_blocks)
1203            },
1204            constants::MAX_FRAGMENT_UNIFORM_COMPONENTS => {
1205                Some(self.base.limits().max_fragment_uniform_components)
1206            },
1207            constants::MAX_PROGRAM_TEXEL_OFFSET => {
1208                Some(self.base.limits().max_program_texel_offset)
1209            },
1210            constants::MAX_SAMPLES => Some(self.base.limits().max_samples),
1211            constants::MAX_UNIFORM_BUFFER_BINDINGS => {
1212                Some(self.base.limits().max_uniform_buffer_bindings)
1213            },
1214            constants::MAX_VARYING_COMPONENTS => Some(self.base.limits().max_varying_components),
1215            constants::MAX_VERTEX_OUTPUT_COMPONENTS => {
1216                Some(self.base.limits().max_vertex_output_components)
1217            },
1218            constants::MAX_VERTEX_UNIFORM_BLOCKS => {
1219                Some(self.base.limits().max_vertex_uniform_blocks)
1220            },
1221            constants::MAX_VERTEX_UNIFORM_COMPONENTS => {
1222                Some(self.base.limits().max_vertex_uniform_components)
1223            },
1224            constants::UNIFORM_BUFFER_OFFSET_ALIGNMENT => {
1225                Some(self.base.limits().uniform_buffer_offset_alignment)
1226            },
1227            _ => None,
1228        };
1229        if let Some(limit) = limit {
1230            rval.set(UInt32Value(limit));
1231            return;
1232        }
1233
1234        self.base.GetParameter(cx, parameter, rval)
1235    }
1236
1237    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1238    fn GetTexParameter(
1239        &self,
1240        cx: &mut JSContext,
1241        target: u32,
1242        pname: u32,
1243        retval: MutableHandleValue,
1244    ) {
1245        self.base.GetTexParameter(cx, target, pname, retval)
1246    }
1247
1248    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1249    fn GetError(&self) -> u32 {
1250        self.base.GetError()
1251    }
1252
1253    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.2>
1254    fn GetContextAttributes(&self) -> Option<WebGLContextAttributes> {
1255        self.base.GetContextAttributes()
1256    }
1257
1258    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.13>
1259    fn IsContextLost(&self) -> bool {
1260        self.base.IsContextLost()
1261    }
1262
1263    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.14>
1264    fn GetSupportedExtensions(&self) -> Option<Vec<DOMString>> {
1265        self.base.GetSupportedExtensions()
1266    }
1267
1268    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.14>
1269    fn GetExtension(
1270        &self,
1271        cx: &mut js::context::JSContext,
1272        name: DOMString,
1273        return_value: MutableHandleObject,
1274    ) {
1275        self.base.GetExtension(cx, name, return_value)
1276    }
1277
1278    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.4>
1279    fn GetFramebufferAttachmentParameter(
1280        &self,
1281        cx: &mut JSContext,
1282        target: u32,
1283        attachment: u32,
1284        pname: u32,
1285        mut rval: MutableHandleValue,
1286    ) {
1287        let fb_slot = match target {
1288            constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
1289                self.base.get_draw_framebuffer_slot()
1290            },
1291            constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
1292            _ => {
1293                self.base.webgl_error(InvalidEnum);
1294                rval.set(NullValue());
1295                return;
1296            },
1297        };
1298
1299        if let Some(fb) = fb_slot.get() {
1300            // A selected framebuffer is bound to the target
1301            handle_potential_webgl_error!(
1302                self.base,
1303                fb.validate_transparent(),
1304                return rval.set(NullValue())
1305            );
1306            handle_potential_webgl_error!(
1307                self.base,
1308                self.get_specific_fb_attachment_param(
1309                    cx,
1310                    &fb,
1311                    target,
1312                    attachment,
1313                    pname,
1314                    rval.reborrow()
1315                ),
1316                rval.set(NullValue())
1317            )
1318        } else {
1319            // The default framebuffer is bound to the target
1320            handle_potential_webgl_error!(
1321                self.base,
1322                self.get_default_fb_attachment_param(attachment, pname, rval.reborrow()),
1323                rval.set(NullValue())
1324            )
1325        }
1326    }
1327
1328    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
1329    fn GetRenderbufferParameter(
1330        &self,
1331        cx: &mut JSContext,
1332        target: u32,
1333        pname: u32,
1334        retval: MutableHandleValue,
1335    ) {
1336        self.base
1337            .GetRenderbufferParameter(cx, target, pname, retval)
1338    }
1339
1340    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1341    fn ActiveTexture(&self, texture: u32) {
1342        self.base.ActiveTexture(texture)
1343    }
1344
1345    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1346    fn BlendColor(&self, r: f32, g: f32, b: f32, a: f32) {
1347        self.base.BlendColor(r, g, b, a)
1348    }
1349
1350    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1351    fn BlendEquation(&self, mode: u32) {
1352        self.base.BlendEquation(mode)
1353    }
1354
1355    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1356    fn BlendEquationSeparate(&self, mode_rgb: u32, mode_alpha: u32) {
1357        self.base.BlendEquationSeparate(mode_rgb, mode_alpha)
1358    }
1359
1360    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1361    fn BlendFunc(&self, src_factor: u32, dest_factor: u32) {
1362        self.base.BlendFunc(src_factor, dest_factor)
1363    }
1364
1365    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1366    fn BlendFuncSeparate(&self, src_rgb: u32, dest_rgb: u32, src_alpha: u32, dest_alpha: u32) {
1367        self.base
1368            .BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha)
1369    }
1370
1371    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1372    fn AttachShader(&self, program: &WebGLProgram, shader: &WebGLShader) {
1373        self.base.AttachShader(program, shader)
1374    }
1375
1376    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1377    fn DetachShader(&self, program: &WebGLProgram, shader: &WebGLShader) {
1378        self.base.DetachShader(program, shader)
1379    }
1380
1381    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1382    fn BindAttribLocation(&self, program: &WebGLProgram, index: u32, name: DOMString) {
1383        self.base.BindAttribLocation(program, index, name)
1384    }
1385
1386    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.2>
1387    fn BindBuffer(&self, cx: &mut JSContext, target: u32, buffer: Option<&WebGLBuffer>) {
1388        let current_vao;
1389        let slot = match target {
1390            constants::COPY_READ_BUFFER => &self.bound_copy_read_buffer,
1391            constants::COPY_WRITE_BUFFER => &self.bound_copy_write_buffer,
1392            constants::PIXEL_PACK_BUFFER => &self.bound_pixel_pack_buffer,
1393            constants::PIXEL_UNPACK_BUFFER => &self.bound_pixel_unpack_buffer,
1394            constants::TRANSFORM_FEEDBACK_BUFFER => &self.bound_transform_feedback_buffer,
1395            constants::UNIFORM_BUFFER => &self.bound_uniform_buffer,
1396            constants::ELEMENT_ARRAY_BUFFER => {
1397                current_vao = self.current_vao(cx);
1398                current_vao.element_array_buffer()
1399            },
1400            _ => return self.base.BindBuffer(cx, target, buffer),
1401        };
1402        self.base.bind_buffer_maybe(slot, target, buffer);
1403    }
1404
1405    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
1406    fn BindFramebuffer(&self, target: u32, framebuffer: Option<&WebGLFramebuffer>) {
1407        handle_potential_webgl_error!(
1408            self.base,
1409            self.base.validate_new_framebuffer_binding(framebuffer),
1410            return
1411        );
1412
1413        let (bind_read, bind_draw) = match target {
1414            constants::FRAMEBUFFER => (true, true),
1415            constants::READ_FRAMEBUFFER => (true, false),
1416            constants::DRAW_FRAMEBUFFER => (false, true),
1417            _ => return self.base.webgl_error(InvalidEnum),
1418        };
1419        if bind_read {
1420            self.base.bind_framebuffer_to(
1421                target,
1422                framebuffer,
1423                self.base.get_read_framebuffer_slot(),
1424            );
1425        }
1426        if bind_draw {
1427            self.base.bind_framebuffer_to(
1428                target,
1429                framebuffer,
1430                self.base.get_draw_framebuffer_slot(),
1431            );
1432        }
1433    }
1434
1435    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
1436    fn BindRenderbuffer(&self, target: u32, renderbuffer: Option<&WebGLRenderbuffer>) {
1437        self.base.BindRenderbuffer(target, renderbuffer)
1438    }
1439
1440    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1441    fn BindTexture(&self, target: u32, texture: Option<&WebGLTexture>) {
1442        self.base.BindTexture(target, texture)
1443    }
1444
1445    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1446    fn GenerateMipmap(&self, target: u32) {
1447        self.base.GenerateMipmap(target)
1448    }
1449
1450    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
1451    fn BufferData_(
1452        &self,
1453        cx: &mut JSContext,
1454        target: u32,
1455        data: Option<ArrayBufferViewOrArrayBuffer>,
1456        usage: u32,
1457    ) {
1458        let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return);
1459        let bound_buffer =
1460            handle_potential_webgl_error!(self.base, self.bound_buffer(cx, target), return);
1461        self.base.buffer_data(target, data, usage, bound_buffer)
1462    }
1463
1464    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
1465    fn BufferData(&self, cx: &mut JSContext, target: u32, size: i64, usage: u32) {
1466        let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return);
1467        let bound_buffer =
1468            handle_potential_webgl_error!(self.base, self.bound_buffer(cx, target), return);
1469        self.base.buffer_data_(target, size, usage, bound_buffer)
1470    }
1471
1472    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.3>
1473    #[expect(unsafe_code)]
1474    fn BufferData__(
1475        &self,
1476        cx: &mut JSContext,
1477        target: u32,
1478        data: CustomAutoRooterGuard<ArrayBufferView>,
1479        usage: u32,
1480        elem_offset: u32,
1481        length: u32,
1482    ) {
1483        let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return);
1484        let bound_buffer =
1485            handle_potential_webgl_error!(self.base, self.bound_buffer(cx, target), return);
1486        let bound_buffer =
1487            handle_potential_webgl_error!(self.base, bound_buffer.ok_or(InvalidOperation), return);
1488
1489        let elem_size = data.get_array_type().byte_size().unwrap();
1490        let elem_count = data.len() / elem_size;
1491        let elem_offset = elem_offset as usize;
1492        let byte_offset = elem_offset * elem_size;
1493
1494        if byte_offset > data.len() {
1495            return self.base.webgl_error(InvalidValue);
1496        }
1497
1498        let copy_count = if length == 0 {
1499            elem_count - elem_offset
1500        } else {
1501            length as usize
1502        };
1503        if copy_count == 0 {
1504            return;
1505        }
1506        let copy_bytes = copy_count * elem_size;
1507
1508        if byte_offset + copy_bytes > data.len() {
1509            return self.base.webgl_error(InvalidValue);
1510        }
1511
1512        let data_end = byte_offset + copy_bytes;
1513        let data: &[u8] = unsafe { &data.as_slice()[byte_offset..data_end] };
1514        handle_potential_webgl_error!(self.base, bound_buffer.buffer_data(target, data, usage));
1515    }
1516
1517    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
1518    fn BufferSubData(
1519        &self,
1520        cx: &mut JSContext,
1521        target: u32,
1522        offset: i64,
1523        data: ArrayBufferViewOrArrayBuffer,
1524    ) {
1525        let bound_buffer =
1526            handle_potential_webgl_error!(self.base, self.bound_buffer(cx, target), return);
1527        self.base
1528            .buffer_sub_data(target, offset, data, bound_buffer)
1529    }
1530
1531    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.3>
1532    #[expect(unsafe_code)]
1533    fn BufferSubData_(
1534        &self,
1535        cx: &mut JSContext,
1536        target: u32,
1537        dst_byte_offset: i64,
1538        src_data: CustomAutoRooterGuard<ArrayBufferView>,
1539        src_elem_offset: u32,
1540        length: u32,
1541    ) {
1542        let bound_buffer =
1543            handle_potential_webgl_error!(self.base, self.bound_buffer(cx, target), return);
1544        let bound_buffer =
1545            handle_potential_webgl_error!(self.base, bound_buffer.ok_or(InvalidOperation), return);
1546
1547        let src_elem_size = src_data.get_array_type().byte_size().unwrap();
1548        let src_elem_count = src_data.len() / src_elem_size;
1549        let src_elem_offset = src_elem_offset as usize;
1550        let src_byte_offset = src_elem_offset * src_elem_size;
1551
1552        if dst_byte_offset < 0 || src_byte_offset > src_data.len() {
1553            return self.base.webgl_error(InvalidValue);
1554        }
1555
1556        let copy_count = if length == 0 {
1557            src_elem_count - src_elem_offset
1558        } else {
1559            length as usize
1560        };
1561        if copy_count == 0 {
1562            return;
1563        }
1564        let copy_bytes = copy_count * src_elem_size;
1565
1566        let dst_byte_offset = dst_byte_offset as usize;
1567        if dst_byte_offset + copy_bytes > bound_buffer.capacity() ||
1568            src_byte_offset + copy_bytes > src_data.len()
1569        {
1570            return self.base.webgl_error(InvalidValue);
1571        }
1572
1573        let (sender, receiver) = generic_channel::channel().unwrap();
1574        self.base.send_command(WebGLCommand::BufferSubData(
1575            target,
1576            dst_byte_offset as isize,
1577            receiver,
1578        ));
1579        let src_end = src_byte_offset + copy_bytes;
1580        let data: &[u8] = unsafe { &src_data.as_slice()[src_byte_offset..src_end] };
1581        let buffer = GenericSharedMemory::from_bytes(data);
1582        sender.send(buffer).unwrap();
1583    }
1584
1585    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.3>
1586    fn CopyBufferSubData(
1587        &self,
1588        cx: &mut JSContext,
1589        read_target: u32,
1590        write_target: u32,
1591        read_offset: i64,
1592        write_offset: i64,
1593        size: i64,
1594    ) {
1595        if read_offset < 0 || write_offset < 0 || size < 0 {
1596            return self.base.webgl_error(InvalidValue);
1597        }
1598
1599        let read_buffer =
1600            handle_potential_webgl_error!(self.base, self.bound_buffer(cx, read_target), return);
1601        let read_buffer =
1602            handle_potential_webgl_error!(self.base, read_buffer.ok_or(InvalidOperation), return);
1603
1604        let write_buffer =
1605            handle_potential_webgl_error!(self.base, self.bound_buffer(cx, write_target), return);
1606        let write_buffer =
1607            handle_potential_webgl_error!(self.base, write_buffer.ok_or(InvalidOperation), return);
1608
1609        let read_until = read_offset + size;
1610        let write_until = write_offset + size;
1611        if read_until as usize > read_buffer.capacity() ||
1612            write_until as usize > write_buffer.capacity()
1613        {
1614            return self.base.webgl_error(InvalidValue);
1615        }
1616
1617        if read_target == write_target {
1618            let is_separate = read_until <= write_offset || write_until <= read_offset;
1619            if !is_separate {
1620                return self.base.webgl_error(InvalidValue);
1621            }
1622        }
1623        let src_is_elemarray = read_buffer
1624            .target()
1625            .is_some_and(|t| t == constants::ELEMENT_ARRAY_BUFFER);
1626        let dst_is_elemarray = write_buffer
1627            .target()
1628            .is_some_and(|t| t == constants::ELEMENT_ARRAY_BUFFER);
1629        if src_is_elemarray != dst_is_elemarray {
1630            return self.base.webgl_error(InvalidOperation);
1631        }
1632
1633        self.base.send_command(WebGLCommand::CopyBufferSubData(
1634            read_target,
1635            write_target,
1636            read_offset,
1637            write_offset,
1638            size,
1639        ));
1640    }
1641
1642    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.3>
1643    #[expect(unsafe_code)]
1644    fn GetBufferSubData(
1645        &self,
1646        cx: &mut JSContext,
1647        target: u32,
1648        src_byte_offset: i64,
1649        mut dst_buffer: CustomAutoRooterGuard<ArrayBufferView>,
1650        dst_elem_offset: u32,
1651        length: u32,
1652    ) {
1653        let bound_buffer =
1654            handle_potential_webgl_error!(self.base, self.bound_buffer(cx, target), return);
1655        let bound_buffer =
1656            handle_potential_webgl_error!(self.base, bound_buffer.ok_or(InvalidOperation), return);
1657
1658        let dst_elem_size = dst_buffer.get_array_type().byte_size().unwrap();
1659        let dst_elem_count = dst_buffer.len() / dst_elem_size;
1660        let dst_elem_offset = dst_elem_offset as usize;
1661        let dst_byte_offset = dst_elem_offset * dst_elem_size;
1662
1663        if src_byte_offset < 0 || dst_byte_offset > dst_buffer.len() {
1664            return self.base.webgl_error(InvalidValue);
1665        }
1666
1667        let copy_count = if length == 0 {
1668            dst_elem_count - dst_elem_offset
1669        } else {
1670            length as usize
1671        };
1672        if copy_count == 0 {
1673            return;
1674        }
1675        let copy_bytes = copy_count * dst_elem_size;
1676
1677        // TODO(mmatyas): Transform Feedback
1678
1679        let src_byte_offset = src_byte_offset as usize;
1680        if src_byte_offset + copy_bytes > bound_buffer.capacity() ||
1681            dst_byte_offset + copy_bytes > dst_buffer.len()
1682        {
1683            return self.base.webgl_error(InvalidValue);
1684        }
1685
1686        let (sender, receiver) = generic_channel::channel().unwrap();
1687        self.base.send_command(WebGLCommand::GetBufferSubData(
1688            target,
1689            src_byte_offset,
1690            copy_bytes,
1691            sender,
1692        ));
1693        let data = receiver.recv().unwrap();
1694        let dst_end = dst_byte_offset + copy_bytes;
1695        unsafe {
1696            dst_buffer.as_mut_slice()[dst_byte_offset..dst_end].copy_from_slice(&data);
1697        }
1698    }
1699
1700    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.6>
1701    #[expect(unsafe_code)]
1702    fn CompressedTexImage2D(
1703        &self,
1704        target: u32,
1705        level: i32,
1706        internal_format: u32,
1707        width: i32,
1708        height: i32,
1709        border: i32,
1710        pixels: CustomAutoRooterGuard<ArrayBufferView>,
1711        src_offset: u32,
1712        src_length_override: u32,
1713    ) {
1714        let mut data = unsafe { pixels.as_slice() };
1715        let start = src_offset as usize;
1716        let end = (src_offset + src_length_override) as usize;
1717        if start > data.len() || end > data.len() {
1718            self.base.webgl_error(InvalidValue);
1719            return;
1720        }
1721        if src_length_override != 0 {
1722            data = &data[start..end];
1723        }
1724        self.base.compressed_tex_image_2d(
1725            target,
1726            level,
1727            internal_format,
1728            width,
1729            height,
1730            border,
1731            data,
1732        )
1733    }
1734
1735    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1736    #[expect(unsafe_code)]
1737    fn CompressedTexSubImage2D(
1738        &self,
1739        target: u32,
1740        level: i32,
1741        xoffset: i32,
1742        yoffset: i32,
1743        width: i32,
1744        height: i32,
1745        format: u32,
1746        pixels: CustomAutoRooterGuard<ArrayBufferView>,
1747        src_offset: u32,
1748        src_length_override: u32,
1749    ) {
1750        let mut data = unsafe { pixels.as_slice() };
1751        let start = src_offset as usize;
1752        let end = (src_offset + src_length_override) as usize;
1753        if start > data.len() || end > data.len() {
1754            self.base.webgl_error(InvalidValue);
1755            return;
1756        }
1757        if src_length_override != 0 {
1758            data = &data[start..end];
1759        }
1760        self.base.compressed_tex_sub_image_2d(
1761            target, level, xoffset, yoffset, width, height, format, data,
1762        )
1763    }
1764
1765    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1766    fn CopyTexImage2D(
1767        &self,
1768        target: u32,
1769        level: i32,
1770        internal_format: u32,
1771        x: i32,
1772        y: i32,
1773        width: i32,
1774        height: i32,
1775        border: i32,
1776    ) {
1777        self.base
1778            .CopyTexImage2D(target, level, internal_format, x, y, width, height, border)
1779    }
1780
1781    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1782    fn CopyTexSubImage2D(
1783        &self,
1784        target: u32,
1785        level: i32,
1786        xoffset: i32,
1787        yoffset: i32,
1788        x: i32,
1789        y: i32,
1790        width: i32,
1791        height: i32,
1792    ) {
1793        self.base
1794            .CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height)
1795    }
1796
1797    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11>
1798    fn Clear(&self, mask: u32) {
1799        self.base.Clear(mask)
1800    }
1801
1802    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1803    fn ClearColor(&self, red: f32, green: f32, blue: f32, alpha: f32) {
1804        self.base.ClearColor(red, green, blue, alpha)
1805    }
1806
1807    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1808    fn ClearDepth(&self, depth: f32) {
1809        self.base.ClearDepth(depth)
1810    }
1811
1812    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1813    fn ClearStencil(&self, stencil: i32) {
1814        self.base.ClearStencil(stencil)
1815    }
1816
1817    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1818    fn ColorMask(&self, r: bool, g: bool, b: bool, a: bool) {
1819        self.base.ColorMask(r, g, b, a)
1820    }
1821
1822    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1823    fn CullFace(&self, mode: u32) {
1824        self.base.CullFace(mode)
1825    }
1826
1827    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1828    fn FrontFace(&self, mode: u32) {
1829        self.base.FrontFace(mode)
1830    }
1831    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1832    fn DepthFunc(&self, func: u32) {
1833        self.base.DepthFunc(func)
1834    }
1835
1836    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1837    fn DepthMask(&self, flag: bool) {
1838        self.base.DepthMask(flag)
1839    }
1840
1841    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1842    fn DepthRange(&self, near: f32, far: f32) {
1843        self.base.DepthRange(near, far)
1844    }
1845
1846    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1847    fn Enable(&self, cap: u32) {
1848        match cap {
1849            constants::RASTERIZER_DISCARD => {
1850                self.enable_rasterizer_discard.set(true);
1851                self.base.send_command(WebGLCommand::Enable(cap));
1852            },
1853            _ => self.base.Enable(cap),
1854        }
1855    }
1856
1857    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1858    fn Disable(&self, cap: u32) {
1859        match cap {
1860            constants::RASTERIZER_DISCARD => {
1861                self.enable_rasterizer_discard.set(false);
1862                self.base.send_command(WebGLCommand::Disable(cap));
1863            },
1864            _ => self.base.Disable(cap),
1865        }
1866    }
1867
1868    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1869    fn CompileShader(&self, shader: &WebGLShader) {
1870        self.base.CompileShader(shader)
1871    }
1872
1873    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
1874    fn CreateBuffer(&self, cx: &mut JSContext) -> Option<DomRoot<WebGLBuffer>> {
1875        self.base.CreateBuffer(cx)
1876    }
1877
1878    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
1879    fn CreateFramebuffer(&self, cx: &mut JSContext) -> Option<DomRoot<WebGLFramebuffer>> {
1880        self.base.CreateFramebuffer(cx)
1881    }
1882
1883    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
1884    fn CreateRenderbuffer(&self, cx: &mut JSContext) -> Option<DomRoot<WebGLRenderbuffer>> {
1885        self.base.CreateRenderbuffer(cx)
1886    }
1887
1888    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1889    fn CreateTexture(&self, cx: &mut JSContext) -> Option<DomRoot<WebGLTexture>> {
1890        self.base.CreateTexture(cx)
1891    }
1892
1893    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1894    fn CreateProgram(&self, cx: &mut JSContext) -> Option<DomRoot<WebGLProgram>> {
1895        self.base.CreateProgram(cx)
1896    }
1897
1898    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1899    fn CreateShader(&self, cx: &mut JSContext, shader_type: u32) -> Option<DomRoot<WebGLShader>> {
1900        self.base.CreateShader(cx, shader_type)
1901    }
1902
1903    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.17>
1904    fn CreateVertexArray(&self, cx: &mut JSContext) -> Option<DomRoot<WebGLVertexArrayObject>> {
1905        self.base.create_vertex_array_webgl2(cx)
1906    }
1907
1908    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
1909    fn DeleteBuffer(&self, cx: &mut JSContext, buffer: Option<&WebGLBuffer>) {
1910        let buffer = match buffer {
1911            Some(buffer) => buffer,
1912            None => return,
1913        };
1914        handle_potential_webgl_error!(self.base, self.base.validate_ownership(buffer), return);
1915        if buffer.is_marked_for_deletion() {
1916            return;
1917        }
1918        self.current_vao(cx).unbind_buffer(buffer);
1919        self.unbind_from(self.base.array_buffer_slot(), buffer);
1920        self.unbind_from(&self.bound_copy_read_buffer, buffer);
1921        self.unbind_from(&self.bound_copy_write_buffer, buffer);
1922        self.unbind_from(&self.bound_pixel_pack_buffer, buffer);
1923        self.unbind_from(&self.bound_pixel_unpack_buffer, buffer);
1924        self.unbind_from(&self.bound_transform_feedback_buffer, buffer);
1925        self.unbind_from(&self.bound_uniform_buffer, buffer);
1926
1927        for binding in self.indexed_uniform_buffer_bindings.iter() {
1928            self.unbind_from(&binding.buffer, buffer);
1929        }
1930        for binding in self.indexed_transform_feedback_buffer_bindings.iter() {
1931            self.unbind_from(&binding.buffer, buffer);
1932        }
1933
1934        buffer.mark_for_deletion(Operation::Infallible);
1935    }
1936
1937    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
1938    fn DeleteFramebuffer(&self, framebuffer: Option<&WebGLFramebuffer>) {
1939        self.base.DeleteFramebuffer(framebuffer)
1940    }
1941
1942    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
1943    fn DeleteRenderbuffer(&self, renderbuffer: Option<&WebGLRenderbuffer>) {
1944        self.base.DeleteRenderbuffer(renderbuffer)
1945    }
1946
1947    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1948    fn DeleteTexture(&self, texture: Option<&WebGLTexture>) {
1949        self.base.DeleteTexture(texture)
1950    }
1951
1952    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1953    fn DeleteProgram(&self, program: Option<&WebGLProgram>) {
1954        self.base.DeleteProgram(program)
1955    }
1956
1957    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1958    fn DeleteShader(&self, shader: Option<&WebGLShader>) {
1959        self.base.DeleteShader(shader)
1960    }
1961
1962    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.17>
1963    fn DeleteVertexArray(&self, cx: &mut JSContext, vertex_array: Option<&WebGLVertexArrayObject>) {
1964        self.base.delete_vertex_array_webgl2(cx, vertex_array);
1965    }
1966
1967    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11>
1968    fn DrawArrays(&self, cx: &mut JSContext, mode: u32, first: i32, count: i32) {
1969        self.validate_uniform_block_for_draw();
1970        self.validate_vertex_attribs_for_draw(cx);
1971        self.base.DrawArrays(cx, mode, first, count)
1972    }
1973
1974    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11>
1975    fn DrawElements(&self, cx: &mut JSContext, mode: u32, count: i32, type_: u32, offset: i64) {
1976        self.validate_uniform_block_for_draw();
1977        self.validate_vertex_attribs_for_draw(cx);
1978        self.base.DrawElements(cx, mode, count, type_, offset)
1979    }
1980
1981    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
1982    fn EnableVertexAttribArray(&self, cx: &mut JSContext, attrib_id: u32) {
1983        self.base.EnableVertexAttribArray(cx, attrib_id)
1984    }
1985
1986    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
1987    fn DisableVertexAttribArray(&self, cx: &mut JSContext, attrib_id: u32) {
1988        self.base.DisableVertexAttribArray(cx, attrib_id)
1989    }
1990
1991    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
1992    fn GetActiveUniform(
1993        &self,
1994        cx: &mut JSContext,
1995        program: &WebGLProgram,
1996        index: u32,
1997    ) -> Option<DomRoot<WebGLActiveInfo>> {
1998        self.base.GetActiveUniform(cx, program, index)
1999    }
2000
2001    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2002    fn GetActiveAttrib(
2003        &self,
2004        cx: &mut JSContext,
2005        program: &WebGLProgram,
2006        index: u32,
2007    ) -> Option<DomRoot<WebGLActiveInfo>> {
2008        self.base.GetActiveAttrib(cx, program, index)
2009    }
2010
2011    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2012    fn GetAttribLocation(&self, program: &WebGLProgram, name: DOMString) -> i32 {
2013        self.base.GetAttribLocation(program, name)
2014    }
2015
2016    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.7>
2017    fn GetFragDataLocation(&self, program: &WebGLProgram, name: DOMString) -> i32 {
2018        handle_potential_webgl_error!(self.base, self.base.validate_ownership(program), return -1);
2019        handle_potential_webgl_error!(self.base, program.get_frag_data_location(name), -1)
2020    }
2021
2022    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2023    fn GetProgramInfoLog(&self, program: &WebGLProgram) -> Option<DOMString> {
2024        self.base.GetProgramInfoLog(program)
2025    }
2026
2027    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2028    fn GetProgramParameter(
2029        &self,
2030        cx: &mut JSContext,
2031        program: &WebGLProgram,
2032        param_id: u32,
2033        mut retval: MutableHandleValue,
2034    ) {
2035        handle_potential_webgl_error!(
2036            self.base,
2037            self.base.validate_ownership(program),
2038            return retval.set(NullValue())
2039        );
2040        if program.is_deleted() {
2041            self.base.webgl_error(InvalidOperation);
2042            return retval.set(NullValue());
2043        }
2044        match param_id {
2045            constants::TRANSFORM_FEEDBACK_VARYINGS => {
2046                retval.set(Int32Value(program.transform_feedback_varyings_length()))
2047            },
2048            constants::TRANSFORM_FEEDBACK_BUFFER_MODE => {
2049                retval.set(Int32Value(program.transform_feedback_buffer_mode()))
2050            },
2051            _ => self.base.GetProgramParameter(cx, program, param_id, retval),
2052        }
2053    }
2054
2055    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2056    fn GetShaderInfoLog(&self, shader: &WebGLShader) -> Option<DOMString> {
2057        self.base.GetShaderInfoLog(shader)
2058    }
2059
2060    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2061    fn GetShaderParameter(
2062        &self,
2063        cx: &mut JSContext,
2064        shader: &WebGLShader,
2065        param_id: u32,
2066        retval: MutableHandleValue,
2067    ) {
2068        self.base.GetShaderParameter(cx, shader, param_id, retval)
2069    }
2070
2071    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2072    fn GetShaderPrecisionFormat(
2073        &self,
2074        cx: &mut JSContext,
2075        shader_type: u32,
2076        precision_type: u32,
2077    ) -> Option<DomRoot<WebGLShaderPrecisionFormat>> {
2078        self.base
2079            .GetShaderPrecisionFormat(cx, shader_type, precision_type)
2080    }
2081
2082    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.2>
2083    fn GetIndexedParameter(
2084        &self,
2085        cx: &mut JSContext,
2086        target: u32,
2087        index: u32,
2088        mut retval: MutableHandleValue,
2089    ) {
2090        let bindings = match target {
2091            constants::TRANSFORM_FEEDBACK_BUFFER_BINDING |
2092            constants::TRANSFORM_FEEDBACK_BUFFER_SIZE |
2093            constants::TRANSFORM_FEEDBACK_BUFFER_START => {
2094                &self.indexed_transform_feedback_buffer_bindings
2095            },
2096            constants::UNIFORM_BUFFER_BINDING |
2097            constants::UNIFORM_BUFFER_SIZE |
2098            constants::UNIFORM_BUFFER_START => &self.indexed_uniform_buffer_bindings,
2099            _ => {
2100                self.base.webgl_error(InvalidEnum);
2101                return retval.set(NullValue());
2102            },
2103        };
2104
2105        let binding = match bindings.get(index as usize) {
2106            Some(binding) => binding,
2107            None => {
2108                self.base.webgl_error(InvalidValue);
2109                return retval.set(NullValue());
2110            },
2111        };
2112
2113        match target {
2114            constants::TRANSFORM_FEEDBACK_BUFFER_BINDING | constants::UNIFORM_BUFFER_BINDING => {
2115                binding.buffer.get().safe_to_jsval(cx, retval)
2116            },
2117            constants::TRANSFORM_FEEDBACK_BUFFER_START | constants::UNIFORM_BUFFER_START => {
2118                retval.set(Int32Value(binding.start.get() as _))
2119            },
2120            constants::TRANSFORM_FEEDBACK_BUFFER_SIZE | constants::UNIFORM_BUFFER_SIZE => {
2121                retval.set(Int32Value(binding.size.get() as _))
2122            },
2123            _ => unreachable!(),
2124        }
2125    }
2126
2127    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2128    fn GetUniformLocation(
2129        &self,
2130        cx: &mut JSContext,
2131        program: &WebGLProgram,
2132        name: DOMString,
2133    ) -> Option<DomRoot<WebGLUniformLocation>> {
2134        self.base.GetUniformLocation(cx, program, name)
2135    }
2136
2137    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2138    fn GetVertexAttrib(
2139        &self,
2140        cx: &mut JSContext,
2141        index: u32,
2142        pname: u32,
2143        retval: MutableHandleValue,
2144    ) {
2145        self.base.GetVertexAttrib(cx, index, pname, retval)
2146    }
2147
2148    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2149    fn GetVertexAttribOffset(&self, cx: &mut JSContext, index: u32, pname: u32) -> i64 {
2150        self.base.GetVertexAttribOffset(cx, index, pname)
2151    }
2152
2153    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2154    fn Hint(&self, target: u32, mode: u32) {
2155        self.base.Hint(target, mode)
2156    }
2157
2158    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
2159    fn IsBuffer(&self, buffer: Option<&WebGLBuffer>) -> bool {
2160        self.base.IsBuffer(buffer)
2161    }
2162
2163    // TODO: We could write this without IPC, recording the calls to `enable` and `disable`.
2164    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.2>
2165    fn IsEnabled(&self, cap: u32) -> bool {
2166        match cap {
2167            constants::RASTERIZER_DISCARD => self.enable_rasterizer_discard.get(),
2168            _ => self.base.IsEnabled(cap),
2169        }
2170    }
2171
2172    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
2173    fn IsFramebuffer(&self, frame_buffer: Option<&WebGLFramebuffer>) -> bool {
2174        self.base.IsFramebuffer(frame_buffer)
2175    }
2176
2177    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2178    fn IsProgram(&self, program: Option<&WebGLProgram>) -> bool {
2179        self.base.IsProgram(program)
2180    }
2181
2182    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
2183    fn IsRenderbuffer(&self, render_buffer: Option<&WebGLRenderbuffer>) -> bool {
2184        self.base.IsRenderbuffer(render_buffer)
2185    }
2186
2187    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2188    fn IsShader(&self, shader: Option<&WebGLShader>) -> bool {
2189        self.base.IsShader(shader)
2190    }
2191
2192    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
2193    fn IsTexture(&self, texture: Option<&WebGLTexture>) -> bool {
2194        self.base.IsTexture(texture)
2195    }
2196
2197    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.17>
2198    fn IsVertexArray(&self, vertex_array: Option<&WebGLVertexArrayObject>) -> bool {
2199        self.base.is_vertex_array_webgl2(vertex_array)
2200    }
2201
2202    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2203    fn LineWidth(&self, width: f32) {
2204        self.base.LineWidth(width)
2205    }
2206
2207    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.2>
2208    fn PixelStorei(&self, param_name: u32, param_value: i32) {
2209        if param_value < 0 {
2210            return self.base.webgl_error(InvalidValue);
2211        }
2212
2213        match param_name {
2214            constants::PACK_ROW_LENGTH => self.texture_pack_row_length.set(param_value as _),
2215            constants::PACK_SKIP_PIXELS => self.texture_pack_skip_pixels.set(param_value as _),
2216            constants::PACK_SKIP_ROWS => self.texture_pack_skip_rows.set(param_value as _),
2217            _ => self.base.PixelStorei(param_name, param_value),
2218        }
2219    }
2220
2221    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2222    fn PolygonOffset(&self, factor: f32, units: f32) {
2223        self.base.PolygonOffset(factor, units)
2224    }
2225
2226    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.12>
2227    fn ReadPixels(
2228        &self,
2229        x: i32,
2230        y: i32,
2231        width: i32,
2232        height: i32,
2233        format: u32,
2234        pixel_type: u32,
2235        mut pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
2236    ) {
2237        let pixels =
2238            handle_potential_webgl_error!(self.base, pixels.as_mut().ok_or(InvalidValue), return);
2239
2240        self.read_pixels_into(x, y, width, height, format, pixel_type, pixels, 0)
2241    }
2242
2243    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.10>
2244    fn ReadPixels_(
2245        &self,
2246        x: i32,
2247        y: i32,
2248        width: i32,
2249        height: i32,
2250        format: u32,
2251        pixel_type: u32,
2252        dst_byte_offset: i64,
2253    ) {
2254        handle_potential_webgl_error!(self.base, self.base.validate_framebuffer(), return);
2255
2256        let dst = match self.bound_pixel_pack_buffer.get() {
2257            Some(buffer) => buffer,
2258            None => return self.base.webgl_error(InvalidOperation),
2259        };
2260
2261        if dst_byte_offset < 0 {
2262            return self.base.webgl_error(InvalidValue);
2263        }
2264        let dst_byte_offset = dst_byte_offset as usize;
2265        if dst_byte_offset > dst.capacity() {
2266            return self.base.webgl_error(InvalidOperation);
2267        }
2268
2269        let ReadPixelsAllowedFormats {
2270            array_types: _,
2271            channels: bytes_per_pixel,
2272        } = match self.calc_read_pixel_formats(pixel_type, format) {
2273            Ok(result) => result,
2274            Err(error) => return self.base.webgl_error(error),
2275        };
2276        if format != constants::RGBA || pixel_type != constants::UNSIGNED_BYTE {
2277            return self.base.webgl_error(InvalidOperation);
2278        }
2279
2280        let ReadPixelsSizes {
2281            row_stride: _,
2282            skipped_bytes,
2283            size,
2284        } = match self.calc_read_pixel_sizes(width, height, bytes_per_pixel) {
2285            Ok(result) => result,
2286            Err(error) => return self.base.webgl_error(error),
2287        };
2288        let dst_end = dst_byte_offset + skipped_bytes + size;
2289        if dst.capacity() < dst_end {
2290            return self.base.webgl_error(InvalidOperation);
2291        }
2292
2293        {
2294            let (fb_width, fb_height) = handle_potential_webgl_error!(
2295                self.base,
2296                self.base
2297                    .get_current_framebuffer_size()
2298                    .ok_or(InvalidOperation),
2299                return
2300            );
2301            let src_origin = Point2D::new(x, y);
2302            let src_size = Size2D::new(width as u32, height as u32);
2303            let fb_size = Size2D::new(fb_width as u32, fb_height as u32);
2304            if pixels::clip(src_origin, src_size.to_u32(), fb_size.to_u32()).is_none() {
2305                return;
2306            }
2307        }
2308        let src_rect = Rect::new(Point2D::new(x, y), Size2D::new(width, height));
2309
2310        self.base.send_command(WebGLCommand::PixelStorei(
2311            constants::PACK_ALIGNMENT,
2312            self.base.get_texture_packing_alignment() as _,
2313        ));
2314        self.base.send_command(WebGLCommand::PixelStorei(
2315            constants::PACK_ROW_LENGTH,
2316            self.texture_pack_row_length.get() as _,
2317        ));
2318        self.base.send_command(WebGLCommand::PixelStorei(
2319            constants::PACK_SKIP_ROWS,
2320            self.texture_pack_skip_rows.get() as _,
2321        ));
2322        self.base.send_command(WebGLCommand::PixelStorei(
2323            constants::PACK_SKIP_PIXELS,
2324            self.texture_pack_skip_pixels.get() as _,
2325        ));
2326        self.base.send_command(WebGLCommand::ReadPixelsPP(
2327            src_rect,
2328            format,
2329            pixel_type,
2330            dst_byte_offset,
2331        ));
2332    }
2333
2334    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.10>
2335    fn ReadPixels__(
2336        &self,
2337        x: i32,
2338        y: i32,
2339        width: i32,
2340        height: i32,
2341        format: u32,
2342        pixel_type: u32,
2343        mut dst: CustomAutoRooterGuard<ArrayBufferView>,
2344        dst_elem_offset: u32,
2345    ) {
2346        self.read_pixels_into(
2347            x,
2348            y,
2349            width,
2350            height,
2351            format,
2352            pixel_type,
2353            &mut dst,
2354            dst_elem_offset,
2355        )
2356    }
2357
2358    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2359    fn SampleCoverage(&self, value: f32, invert: bool) {
2360        self.base.SampleCoverage(value, invert)
2361    }
2362
2363    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4>
2364    fn Scissor(&self, x: i32, y: i32, width: i32, height: i32) {
2365        self.base.Scissor(x, y, width, height)
2366    }
2367
2368    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2369    fn StencilFunc(&self, func: u32, ref_: i32, mask: u32) {
2370        self.base.StencilFunc(func, ref_, mask)
2371    }
2372
2373    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2374    fn StencilFuncSeparate(&self, face: u32, func: u32, ref_: i32, mask: u32) {
2375        self.base.StencilFuncSeparate(face, func, ref_, mask)
2376    }
2377
2378    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2379    fn StencilMask(&self, mask: u32) {
2380        self.base.StencilMask(mask)
2381    }
2382
2383    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2384    fn StencilMaskSeparate(&self, face: u32, mask: u32) {
2385        self.base.StencilMaskSeparate(face, mask)
2386    }
2387
2388    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2389    fn StencilOp(&self, fail: u32, zfail: u32, zpass: u32) {
2390        self.base.StencilOp(fail, zfail, zpass)
2391    }
2392
2393    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2394    fn StencilOpSeparate(&self, face: u32, fail: u32, zfail: u32, zpass: u32) {
2395        self.base.StencilOpSeparate(face, fail, zfail, zpass)
2396    }
2397
2398    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2399    fn LinkProgram(&self, program: &WebGLProgram) {
2400        self.base.LinkProgram(program)
2401    }
2402
2403    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2404    fn ShaderSource(&self, shader: &WebGLShader, source: DOMString) {
2405        self.base.ShaderSource(shader, source)
2406    }
2407
2408    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2409    fn GetShaderSource(&self, shader: &WebGLShader) -> Option<DOMString> {
2410        self.base.GetShaderSource(shader)
2411    }
2412
2413    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2414    fn Uniform1f(&self, location: Option<&WebGLUniformLocation>, val: f32) {
2415        self.base.Uniform1f(location, val)
2416    }
2417
2418    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2419    fn Uniform1i(&self, location: Option<&WebGLUniformLocation>, val: i32) {
2420        self.base.Uniform1i(location, val)
2421    }
2422
2423    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2424    fn Uniform1iv(
2425        &self,
2426        location: Option<&WebGLUniformLocation>,
2427        v: Int32ArrayOrLongSequence,
2428        src_offset: u32,
2429        src_length: u32,
2430    ) {
2431        self.base.uniform1iv(location, v, src_offset, src_length)
2432    }
2433
2434    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
2435    fn Uniform1ui(&self, location: Option<&WebGLUniformLocation>, val: u32) {
2436        self.base.with_location(location, |location| {
2437            match location.type_() {
2438                constants::BOOL | constants::UNSIGNED_INT => (),
2439                _ => return Err(InvalidOperation),
2440            }
2441            self.base
2442                .send_command(WebGLCommand::Uniform1ui(location.id(), val));
2443            Ok(())
2444        });
2445    }
2446
2447    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
2448    fn Uniform1uiv(
2449        &self,
2450        location: Option<&WebGLUniformLocation>,
2451        val: Uint32ArrayOrUnsignedLongSequence,
2452        src_offset: u32,
2453        src_length: u32,
2454    ) {
2455        self.base.with_location(location, |location| {
2456            match location.type_() {
2457                constants::BOOL |
2458                constants::UNSIGNED_INT |
2459                constants::SAMPLER_2D |
2460                constants::SAMPLER_2D_ARRAY |
2461                constants::SAMPLER_3D |
2462                constants::SAMPLER_CUBE => {},
2463                _ => return Err(InvalidOperation),
2464            }
2465
2466            let val = self.uniform_vec_section_uint(val, src_offset, src_length, 1, location)?;
2467
2468            match location.type_() {
2469                constants::SAMPLER_2D | constants::SAMPLER_CUBE => {
2470                    for &v in val
2471                        .iter()
2472                        .take(cmp::min(location.size().unwrap_or(1) as usize, val.len()))
2473                    {
2474                        if v >= self.base.limits().max_combined_texture_image_units {
2475                            return Err(InvalidValue);
2476                        }
2477                    }
2478                },
2479                _ => {},
2480            }
2481            self.base
2482                .send_command(WebGLCommand::Uniform1uiv(location.id(), val));
2483            Ok(())
2484        });
2485    }
2486
2487    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2488    fn Uniform1fv(
2489        &self,
2490        location: Option<&WebGLUniformLocation>,
2491        v: Float32ArrayOrUnrestrictedFloatSequence,
2492        src_offset: u32,
2493        src_length: u32,
2494    ) {
2495        self.base.uniform1fv(location, v, src_offset, src_length);
2496    }
2497
2498    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2499    fn Uniform2f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32) {
2500        self.base.Uniform2f(location, x, y)
2501    }
2502
2503    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2504    fn Uniform2fv(
2505        &self,
2506        location: Option<&WebGLUniformLocation>,
2507        v: Float32ArrayOrUnrestrictedFloatSequence,
2508        src_offset: u32,
2509        src_length: u32,
2510    ) {
2511        self.base.uniform2fv(location, v, src_offset, src_length);
2512    }
2513
2514    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2515    fn Uniform2i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32) {
2516        self.base.Uniform2i(location, x, y)
2517    }
2518
2519    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2520    fn Uniform2iv(
2521        &self,
2522        location: Option<&WebGLUniformLocation>,
2523        v: Int32ArrayOrLongSequence,
2524        src_offset: u32,
2525        src_length: u32,
2526    ) {
2527        self.base.uniform2iv(location, v, src_offset, src_length)
2528    }
2529
2530    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
2531    fn Uniform2ui(&self, location: Option<&WebGLUniformLocation>, x: u32, y: u32) {
2532        self.base.with_location(location, |location| {
2533            match location.type_() {
2534                constants::BOOL_VEC2 | constants::UNSIGNED_INT_VEC2 => {},
2535                _ => return Err(InvalidOperation),
2536            }
2537            self.base
2538                .send_command(WebGLCommand::Uniform2ui(location.id(), x, y));
2539            Ok(())
2540        });
2541    }
2542
2543    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
2544    fn Uniform2uiv(
2545        &self,
2546        location: Option<&WebGLUniformLocation>,
2547        val: Uint32ArrayOrUnsignedLongSequence,
2548        src_offset: u32,
2549        src_length: u32,
2550    ) {
2551        self.base.with_location(location, |location| {
2552            match location.type_() {
2553                constants::BOOL_VEC2 | constants::UNSIGNED_INT_VEC2 => {},
2554                _ => return Err(InvalidOperation),
2555            }
2556            let val = self.uniform_vec_section_uint(val, src_offset, src_length, 2, location)?;
2557            self.base
2558                .send_command(WebGLCommand::Uniform2uiv(location.id(), val));
2559            Ok(())
2560        });
2561    }
2562
2563    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2564    fn Uniform3f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32) {
2565        self.base.Uniform3f(location, x, y, z)
2566    }
2567
2568    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2569    fn Uniform3fv(
2570        &self,
2571        location: Option<&WebGLUniformLocation>,
2572        v: Float32ArrayOrUnrestrictedFloatSequence,
2573        src_offset: u32,
2574        src_length: u32,
2575    ) {
2576        self.base.uniform3fv(location, v, src_offset, src_length);
2577    }
2578
2579    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2580    fn Uniform3i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32) {
2581        self.base.Uniform3i(location, x, y, z)
2582    }
2583
2584    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2585    fn Uniform3iv(
2586        &self,
2587        location: Option<&WebGLUniformLocation>,
2588        v: Int32ArrayOrLongSequence,
2589        src_offset: u32,
2590        src_length: u32,
2591    ) {
2592        self.base.uniform3iv(location, v, src_offset, src_length)
2593    }
2594
2595    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
2596    fn Uniform3ui(&self, location: Option<&WebGLUniformLocation>, x: u32, y: u32, z: u32) {
2597        self.base.with_location(location, |location| {
2598            match location.type_() {
2599                constants::BOOL_VEC3 | constants::UNSIGNED_INT_VEC3 => {},
2600                _ => return Err(InvalidOperation),
2601            }
2602            self.base
2603                .send_command(WebGLCommand::Uniform3ui(location.id(), x, y, z));
2604            Ok(())
2605        });
2606    }
2607
2608    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
2609    fn Uniform3uiv(
2610        &self,
2611        location: Option<&WebGLUniformLocation>,
2612        val: Uint32ArrayOrUnsignedLongSequence,
2613        src_offset: u32,
2614        src_length: u32,
2615    ) {
2616        self.base.with_location(location, |location| {
2617            match location.type_() {
2618                constants::BOOL_VEC3 | constants::UNSIGNED_INT_VEC3 => {},
2619                _ => return Err(InvalidOperation),
2620            }
2621            let val = self.uniform_vec_section_uint(val, src_offset, src_length, 3, location)?;
2622            self.base
2623                .send_command(WebGLCommand::Uniform3uiv(location.id(), val));
2624            Ok(())
2625        });
2626    }
2627
2628    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2629    fn Uniform4i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32, w: i32) {
2630        self.base.Uniform4i(location, x, y, z, w)
2631    }
2632
2633    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2634    fn Uniform4iv(
2635        &self,
2636        location: Option<&WebGLUniformLocation>,
2637        v: Int32ArrayOrLongSequence,
2638        src_offset: u32,
2639        src_length: u32,
2640    ) {
2641        self.base.uniform4iv(location, v, src_offset, src_length)
2642    }
2643
2644    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
2645    fn Uniform4ui(&self, location: Option<&WebGLUniformLocation>, x: u32, y: u32, z: u32, w: u32) {
2646        self.base.with_location(location, |location| {
2647            match location.type_() {
2648                constants::BOOL_VEC4 | constants::UNSIGNED_INT_VEC4 => {},
2649                _ => return Err(InvalidOperation),
2650            }
2651            self.base
2652                .send_command(WebGLCommand::Uniform4ui(location.id(), x, y, z, w));
2653            Ok(())
2654        });
2655    }
2656
2657    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
2658    fn Uniform4uiv(
2659        &self,
2660        location: Option<&WebGLUniformLocation>,
2661        val: Uint32ArrayOrUnsignedLongSequence,
2662        src_offset: u32,
2663        src_length: u32,
2664    ) {
2665        self.base.with_location(location, |location| {
2666            match location.type_() {
2667                constants::BOOL_VEC4 | constants::UNSIGNED_INT_VEC4 => {},
2668                _ => return Err(InvalidOperation),
2669            }
2670            let val = self.uniform_vec_section_uint(val, src_offset, src_length, 4, location)?;
2671            self.base
2672                .send_command(WebGLCommand::Uniform4uiv(location.id(), val));
2673            Ok(())
2674        });
2675    }
2676
2677    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2678    fn Uniform4f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32, w: f32) {
2679        self.base.Uniform4f(location, x, y, z, w)
2680    }
2681
2682    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2683    fn Uniform4fv(
2684        &self,
2685        location: Option<&WebGLUniformLocation>,
2686        v: Float32ArrayOrUnrestrictedFloatSequence,
2687        src_offset: u32,
2688        src_length: u32,
2689    ) {
2690        self.base.uniform4fv(location, v, src_offset, src_length);
2691    }
2692
2693    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2694    fn UniformMatrix2fv(
2695        &self,
2696        location: Option<&WebGLUniformLocation>,
2697        transpose: bool,
2698        v: Float32ArrayOrUnrestrictedFloatSequence,
2699        src_offset: u32,
2700        src_length: u32,
2701    ) {
2702        self.base
2703            .uniform_matrix_2fv(location, transpose, v, src_offset, src_length)
2704    }
2705
2706    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2707    fn UniformMatrix3fv(
2708        &self,
2709        location: Option<&WebGLUniformLocation>,
2710        transpose: bool,
2711        v: Float32ArrayOrUnrestrictedFloatSequence,
2712        src_offset: u32,
2713        src_length: u32,
2714    ) {
2715        self.base
2716            .uniform_matrix_3fv(location, transpose, v, src_offset, src_length)
2717    }
2718
2719    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2720    fn UniformMatrix4fv(
2721        &self,
2722        location: Option<&WebGLUniformLocation>,
2723        transpose: bool,
2724        v: Float32ArrayOrUnrestrictedFloatSequence,
2725        src_offset: u32,
2726        src_length: u32,
2727    ) {
2728        self.base
2729            .uniform_matrix_4fv(location, transpose, v, src_offset, src_length)
2730    }
2731
2732    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
2733    fn UniformMatrix3x2fv(
2734        &self,
2735        location: Option<&WebGLUniformLocation>,
2736        transpose: bool,
2737        val: Float32ArrayOrUnrestrictedFloatSequence,
2738        src_offset: u32,
2739        src_length: u32,
2740    ) {
2741        self.base.with_location(location, |location| {
2742            match location.type_() {
2743                constants::FLOAT_MAT3x2 => {},
2744                _ => return Err(InvalidOperation),
2745            }
2746            let val = self.base.uniform_matrix_section(
2747                val,
2748                src_offset,
2749                src_length,
2750                transpose,
2751                3 * 2,
2752                location,
2753            )?;
2754            self.base
2755                .send_command(WebGLCommand::UniformMatrix3x2fv(location.id(), val));
2756            Ok(())
2757        });
2758    }
2759
2760    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
2761    fn UniformMatrix4x2fv(
2762        &self,
2763        location: Option<&WebGLUniformLocation>,
2764        transpose: bool,
2765        val: Float32ArrayOrUnrestrictedFloatSequence,
2766        src_offset: u32,
2767        src_length: u32,
2768    ) {
2769        self.base.with_location(location, |location| {
2770            match location.type_() {
2771                constants::FLOAT_MAT4x2 => {},
2772                _ => return Err(InvalidOperation),
2773            }
2774            let val = self.base.uniform_matrix_section(
2775                val,
2776                src_offset,
2777                src_length,
2778                transpose,
2779                4 * 2,
2780                location,
2781            )?;
2782            self.base
2783                .send_command(WebGLCommand::UniformMatrix4x2fv(location.id(), val));
2784            Ok(())
2785        });
2786    }
2787
2788    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
2789    fn UniformMatrix2x3fv(
2790        &self,
2791        location: Option<&WebGLUniformLocation>,
2792        transpose: bool,
2793        val: Float32ArrayOrUnrestrictedFloatSequence,
2794        src_offset: u32,
2795        src_length: u32,
2796    ) {
2797        self.base.with_location(location, |location| {
2798            match location.type_() {
2799                constants::FLOAT_MAT2x3 => {},
2800                _ => return Err(InvalidOperation),
2801            }
2802            let val = self.base.uniform_matrix_section(
2803                val,
2804                src_offset,
2805                src_length,
2806                transpose,
2807                2 * 3,
2808                location,
2809            )?;
2810            self.base
2811                .send_command(WebGLCommand::UniformMatrix2x3fv(location.id(), val));
2812            Ok(())
2813        });
2814    }
2815
2816    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
2817    fn UniformMatrix4x3fv(
2818        &self,
2819        location: Option<&WebGLUniformLocation>,
2820        transpose: bool,
2821        val: Float32ArrayOrUnrestrictedFloatSequence,
2822        src_offset: u32,
2823        src_length: u32,
2824    ) {
2825        self.base.with_location(location, |location| {
2826            match location.type_() {
2827                constants::FLOAT_MAT4x3 => {},
2828                _ => return Err(InvalidOperation),
2829            }
2830            let val = self.base.uniform_matrix_section(
2831                val,
2832                src_offset,
2833                src_length,
2834                transpose,
2835                4 * 3,
2836                location,
2837            )?;
2838            self.base
2839                .send_command(WebGLCommand::UniformMatrix4x3fv(location.id(), val));
2840            Ok(())
2841        });
2842    }
2843
2844    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
2845    fn UniformMatrix2x4fv(
2846        &self,
2847        location: Option<&WebGLUniformLocation>,
2848        transpose: bool,
2849        val: Float32ArrayOrUnrestrictedFloatSequence,
2850        src_offset: u32,
2851        src_length: u32,
2852    ) {
2853        self.base.with_location(location, |location| {
2854            match location.type_() {
2855                constants::FLOAT_MAT2x4 => {},
2856                _ => return Err(InvalidOperation),
2857            }
2858            let val = self.base.uniform_matrix_section(
2859                val,
2860                src_offset,
2861                src_length,
2862                transpose,
2863                2 * 4,
2864                location,
2865            )?;
2866            self.base
2867                .send_command(WebGLCommand::UniformMatrix2x4fv(location.id(), val));
2868            Ok(())
2869        });
2870    }
2871
2872    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
2873    fn UniformMatrix3x4fv(
2874        &self,
2875        location: Option<&WebGLUniformLocation>,
2876        transpose: bool,
2877        val: Float32ArrayOrUnrestrictedFloatSequence,
2878        src_offset: u32,
2879        src_length: u32,
2880    ) {
2881        self.base.with_location(location, |location| {
2882            match location.type_() {
2883                constants::FLOAT_MAT3x4 => {},
2884                _ => return Err(InvalidOperation),
2885            }
2886            let val = self.base.uniform_matrix_section(
2887                val,
2888                src_offset,
2889                src_length,
2890                transpose,
2891                3 * 4,
2892                location,
2893            )?;
2894            self.base
2895                .send_command(WebGLCommand::UniformMatrix3x4fv(location.id(), val));
2896            Ok(())
2897        });
2898    }
2899
2900    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
2901    #[expect(unsafe_code)]
2902    fn GetUniform(
2903        &self,
2904        cx: &mut JSContext,
2905        program: &WebGLProgram,
2906        location: &WebGLUniformLocation,
2907        mut retval: MutableHandleValue,
2908    ) {
2909        handle_potential_webgl_error!(
2910            self.base,
2911            self.base.uniform_check_program(program, location),
2912            return retval.set(NullValue())
2913        );
2914
2915        let triple = (&*self.base, program.id(), location.id());
2916
2917        match location.type_() {
2918            constants::UNSIGNED_INT => retval.set(UInt32Value(uniform_get(
2919                triple,
2920                WebGLCommand::GetUniformUint,
2921            ))),
2922            constants::UNSIGNED_INT_VEC2 => unsafe {
2923                uniform_typed::<Uint32>(
2924                    cx,
2925                    &uniform_get(triple, WebGLCommand::GetUniformUint2),
2926                    retval,
2927                )
2928            },
2929            constants::UNSIGNED_INT_VEC3 => unsafe {
2930                uniform_typed::<Uint32>(
2931                    cx,
2932                    &uniform_get(triple, WebGLCommand::GetUniformUint3),
2933                    retval,
2934                )
2935            },
2936            constants::UNSIGNED_INT_VEC4 => unsafe {
2937                uniform_typed::<Uint32>(
2938                    cx,
2939                    &uniform_get(triple, WebGLCommand::GetUniformUint4),
2940                    retval,
2941                )
2942            },
2943            constants::FLOAT_MAT2x3 => unsafe {
2944                uniform_typed::<Float32>(
2945                    cx,
2946                    &uniform_get(triple, WebGLCommand::GetUniformFloat2x3),
2947                    retval,
2948                )
2949            },
2950            constants::FLOAT_MAT2x4 => unsafe {
2951                uniform_typed::<Float32>(
2952                    cx,
2953                    &uniform_get(triple, WebGLCommand::GetUniformFloat2x4),
2954                    retval,
2955                )
2956            },
2957            constants::FLOAT_MAT3x2 => unsafe {
2958                uniform_typed::<Float32>(
2959                    cx,
2960                    &uniform_get(triple, WebGLCommand::GetUniformFloat3x2),
2961                    retval,
2962                )
2963            },
2964            constants::FLOAT_MAT3x4 => unsafe {
2965                uniform_typed::<Float32>(
2966                    cx,
2967                    &uniform_get(triple, WebGLCommand::GetUniformFloat3x4),
2968                    retval,
2969                )
2970            },
2971            constants::FLOAT_MAT4x2 => unsafe {
2972                uniform_typed::<Float32>(
2973                    cx,
2974                    &uniform_get(triple, WebGLCommand::GetUniformFloat4x2),
2975                    retval,
2976                )
2977            },
2978            constants::FLOAT_MAT4x3 => unsafe {
2979                uniform_typed::<Float32>(
2980                    cx,
2981                    &uniform_get(triple, WebGLCommand::GetUniformFloat4x3),
2982                    retval,
2983                )
2984            },
2985            constants::SAMPLER_3D | constants::SAMPLER_2D_ARRAY => {
2986                retval.set(Int32Value(uniform_get(triple, WebGLCommand::GetUniformInt)))
2987            },
2988            _ => self.base.GetUniform(cx, program, location, retval),
2989        }
2990    }
2991
2992    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2993    fn UseProgram(&self, program: Option<&WebGLProgram>) {
2994        self.base.UseProgram(program)
2995    }
2996
2997    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2998    fn ValidateProgram(&self, program: &WebGLProgram) {
2999        self.base.ValidateProgram(program)
3000    }
3001
3002    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3003    fn VertexAttrib1f(&self, cx: &mut JSContext, indx: u32, x: f32) {
3004        self.base.VertexAttrib1f(cx, indx, x)
3005    }
3006
3007    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3008    fn VertexAttrib1fv(
3009        &self,
3010        cx: &mut JSContext,
3011        indx: u32,
3012        v: Float32ArrayOrUnrestrictedFloatSequence,
3013    ) {
3014        self.base.VertexAttrib1fv(cx, indx, v)
3015    }
3016
3017    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3018    fn VertexAttrib2f(&self, cx: &mut JSContext, indx: u32, x: f32, y: f32) {
3019        self.base.VertexAttrib2f(cx, indx, x, y)
3020    }
3021
3022    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3023    fn VertexAttrib2fv(
3024        &self,
3025        cx: &mut JSContext,
3026        indx: u32,
3027        v: Float32ArrayOrUnrestrictedFloatSequence,
3028    ) {
3029        self.base.VertexAttrib2fv(cx, indx, v)
3030    }
3031
3032    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3033    fn VertexAttrib3f(&self, cx: &mut JSContext, indx: u32, x: f32, y: f32, z: f32) {
3034        self.base.VertexAttrib3f(cx, indx, x, y, z)
3035    }
3036
3037    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3038    fn VertexAttrib3fv(
3039        &self,
3040        cx: &mut JSContext,
3041        indx: u32,
3042        v: Float32ArrayOrUnrestrictedFloatSequence,
3043    ) {
3044        self.base.VertexAttrib3fv(cx, indx, v)
3045    }
3046
3047    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3048    fn VertexAttrib4f(&self, cx: &mut JSContext, indx: u32, x: f32, y: f32, z: f32, w: f32) {
3049        self.base.VertexAttrib4f(cx, indx, x, y, z, w)
3050    }
3051
3052    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3053    fn VertexAttrib4fv(
3054        &self,
3055        cx: &mut JSContext,
3056        indx: u32,
3057        v: Float32ArrayOrUnrestrictedFloatSequence,
3058    ) {
3059        self.base.VertexAttrib4fv(cx, indx, v)
3060    }
3061
3062    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
3063    fn VertexAttribI4i(&self, cx: &mut JSContext, index: u32, x: i32, y: i32, z: i32, w: i32) {
3064        self.vertex_attrib_i(cx, index, x, y, z, w)
3065    }
3066
3067    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
3068    fn VertexAttribI4iv(&self, cx: &mut JSContext, index: u32, v: Int32ArrayOrLongSequence) {
3069        let values = match v {
3070            Int32ArrayOrLongSequence::Int32Array(v) => v.to_vec(),
3071            Int32ArrayOrLongSequence::LongSequence(v) => v,
3072        };
3073        if values.len() < 4 {
3074            return self.base.webgl_error(InvalidValue);
3075        }
3076        self.vertex_attrib_i(cx, index, values[0], values[1], values[2], values[3]);
3077    }
3078
3079    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
3080    fn VertexAttribI4ui(&self, cx: &mut JSContext, index: u32, x: u32, y: u32, z: u32, w: u32) {
3081        self.vertex_attrib_u(cx, index, x, y, z, w)
3082    }
3083
3084    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
3085    fn VertexAttribI4uiv(
3086        &self,
3087        cx: &mut JSContext,
3088        index: u32,
3089        v: Uint32ArrayOrUnsignedLongSequence,
3090    ) {
3091        let values = match v {
3092            Uint32ArrayOrUnsignedLongSequence::Uint32Array(v) => v.to_vec(),
3093            Uint32ArrayOrUnsignedLongSequence::UnsignedLongSequence(v) => v,
3094        };
3095        if values.len() < 4 {
3096            return self.base.webgl_error(InvalidValue);
3097        }
3098        self.vertex_attrib_u(cx, index, values[0], values[1], values[2], values[3]);
3099    }
3100
3101    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3102    fn VertexAttribPointer(
3103        &self,
3104        cx: &mut JSContext,
3105        attrib_id: u32,
3106        size: i32,
3107        data_type: u32,
3108        normalized: bool,
3109        stride: i32,
3110        offset: i64,
3111    ) {
3112        self.base
3113            .VertexAttribPointer(cx, attrib_id, size, data_type, normalized, stride, offset)
3114    }
3115
3116    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.8>
3117    fn VertexAttribIPointer(
3118        &self,
3119        cx: &mut JSContext,
3120        index: u32,
3121        size: i32,
3122        type_: u32,
3123        stride: i32,
3124        offset: i64,
3125    ) {
3126        match type_ {
3127            constants::BYTE |
3128            constants::UNSIGNED_BYTE |
3129            constants::SHORT |
3130            constants::UNSIGNED_SHORT |
3131            constants::INT |
3132            constants::UNSIGNED_INT => {},
3133            _ => return self.base.webgl_error(InvalidEnum),
3134        };
3135        self.base
3136            .VertexAttribPointer(cx, index, size, type_, false, stride, offset)
3137    }
3138
3139    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4>
3140    fn Viewport(&self, x: i32, y: i32, width: i32, height: i32) {
3141        self.base.Viewport(x, y, width, height)
3142    }
3143
3144    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.6>
3145    ///
3146    /// Allocates and initializes the specified mipmap level of a three-dimensional or
3147    /// two-dimensional array texture.
3148    #[expect(unsafe_code)]
3149    fn TexImage3D(
3150        &self,
3151        target: u32,
3152        level: i32,
3153        internal_format: i32,
3154        width: i32,
3155        height: i32,
3156        depth: i32,
3157        border: i32,
3158        format: u32,
3159        type_: u32,
3160        src_data: CustomAutoRooterGuard<Option<ArrayBufferView>>,
3161    ) -> Fallible<()> {
3162        // If a WebGLBuffer is bound to the PIXEL_UNPACK_BUFFER target,
3163        // generates an INVALID_OPERATION error.
3164        if self.bound_pixel_unpack_buffer.get().is_some() {
3165            self.base.webgl_error(InvalidOperation);
3166            return Ok(());
3167        }
3168
3169        // If type is specified as FLOAT_32_UNSIGNED_INT_24_8_REV, srcData must be null;
3170        // otherwise, generates an INVALID_OPERATION error.
3171        if type_ == constants::FLOAT_32_UNSIGNED_INT_24_8_REV && src_data.is_some() {
3172            self.base.webgl_error(InvalidOperation);
3173            return Ok(());
3174        }
3175
3176        if border != 0 {
3177            self.base.webgl_error(InvalidValue);
3178            return Ok(());
3179        }
3180
3181        let Ok(TexImage3DValidatorResult {
3182            width,
3183            height,
3184            depth,
3185            level,
3186            border,
3187            texture,
3188            target,
3189            internal_format,
3190            format,
3191            data_type,
3192        }) = TexImage3DValidator::new(
3193            &self.base,
3194            target,
3195            level,
3196            internal_format as u32,
3197            width,
3198            height,
3199            depth,
3200            border,
3201            format,
3202            type_,
3203            &src_data,
3204        )
3205        .validate()
3206        else {
3207            return Ok(());
3208        };
3209
3210        // TODO: If pixel store parameter constraints are not met, generates an INVALID_OPERATION error.
3211
3212        let unpacking_alignment = self.base.texture_unpacking_alignment();
3213        let element_size = data_type.element_size();
3214        let components_per_element = data_type.components_per_element();
3215        let components = format.components();
3216        // NOTE: width, height and depth are positive or zero due to validate()
3217        let expected_byte_len = if height == 0 {
3218            0
3219        } else {
3220            // We need to be careful here to not count unpack
3221            // alignment at the end of the image, otherwise (for
3222            // example) passing a single byte for uploading a 1x1x1
3223            // GL_ALPHA/GL_UNSIGNED_BYTE texture would throw an error.
3224            let cpp = element_size * components / components_per_element;
3225            let mut padding_bytes = (cpp * width) % unpacking_alignment;
3226            if padding_bytes > 0 {
3227                padding_bytes = unpacking_alignment - padding_bytes;
3228            }
3229            let bytes_per_row = cpp * width + padding_bytes;
3230            let bytes_last_row = cpp * width;
3231            let bytes_per_image = bytes_per_row * height;
3232            let bytes_last_image = bytes_per_row * (height - 1) + bytes_last_row;
3233            bytes_per_image * (depth - 1) + bytes_last_image
3234        };
3235
3236        // If srcData is null, a buffer of sufficient size initialized to 0 is passed.
3237        let buff = match *src_data {
3238            Some(ref data) => GenericSharedMemory::from_bytes(unsafe { data.as_slice() }),
3239            None => GenericSharedMemory::from_byte(0, expected_byte_len as usize),
3240        };
3241        if buff.len() < expected_byte_len as usize {
3242            return {
3243                self.base.webgl_error(InvalidOperation);
3244                Ok(())
3245            };
3246        }
3247        let (alpha_treatment, y_axis_treatment) =
3248            self.base.get_current_unpack_state(Alpha::NotPremultiplied);
3249        // If UNPACK_FLIP_Y_WEBGL or UNPACK_PREMULTIPLY_ALPHA_WEBGL is set to true, texImage3D and texSubImage3D
3250        // generate an INVALID_OPERATION error if they upload data from a PIXEL_UNPACK_BUFFER or a non-null client
3251        // side ArrayBufferView.
3252        if let (Some(AlphaTreatment::Premultiply), YAxisTreatment::Flipped) =
3253            (alpha_treatment, y_axis_treatment) &&
3254            src_data.is_some()
3255        {
3256            self.base.webgl_error(InvalidOperation);
3257            return Ok(());
3258        }
3259        let tex_source = TexPixels::from_array(
3260            buff,
3261            Size2D::new(width, height),
3262            alpha_treatment,
3263            y_axis_treatment,
3264        );
3265
3266        self.tex_image_3d(
3267            &texture,
3268            target,
3269            data_type,
3270            internal_format,
3271            format,
3272            level,
3273            width,
3274            height,
3275            depth,
3276            border,
3277            unpacking_alignment,
3278            tex_source,
3279        );
3280        Ok(())
3281    }
3282
3283    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
3284    fn TexImage2D(
3285        &self,
3286        target: u32,
3287        level: i32,
3288        internal_format: i32,
3289        width: i32,
3290        height: i32,
3291        border: i32,
3292        format: u32,
3293        data_type: u32,
3294        pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
3295    ) -> Fallible<()> {
3296        self.base.TexImage2D(
3297            target,
3298            level,
3299            internal_format,
3300            width,
3301            height,
3302            border,
3303            format,
3304            data_type,
3305            pixels,
3306        )
3307    }
3308
3309    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
3310    fn TexImage2D_(
3311        &self,
3312        target: u32,
3313        level: i32,
3314        internal_format: i32,
3315        format: u32,
3316        data_type: u32,
3317        source: TexImageSource,
3318    ) -> ErrorResult {
3319        self.base
3320            .TexImage2D_(target, level, internal_format, format, data_type, source)
3321    }
3322
3323    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.6>
3324    fn TexImage2D__(
3325        &self,
3326        target: u32,
3327        level: i32,
3328        internalformat: i32,
3329        width: i32,
3330        height: i32,
3331        border: i32,
3332        format: u32,
3333        type_: u32,
3334        pbo_offset: i64,
3335    ) -> Fallible<()> {
3336        let pixel_unpack_buffer = match self.bound_pixel_unpack_buffer.get() {
3337            Some(pixel_unpack_buffer) => pixel_unpack_buffer,
3338            None => {
3339                self.base.webgl_error(InvalidOperation);
3340                return Ok(());
3341            },
3342        };
3343
3344        if let Some(tf_buffer) = self.bound_transform_feedback_buffer.get() &&
3345            pixel_unpack_buffer == tf_buffer
3346        {
3347            self.base.webgl_error(InvalidOperation);
3348            return Ok(());
3349        }
3350
3351        if pbo_offset < 0 || pbo_offset as usize > pixel_unpack_buffer.capacity() {
3352            self.base.webgl_error(InvalidValue);
3353            return Ok(());
3354        }
3355
3356        let unpacking_alignment = self.base.texture_unpacking_alignment();
3357
3358        let validator = TexImage2DValidator::new(
3359            &self.base,
3360            target,
3361            level,
3362            internalformat as u32,
3363            width,
3364            height,
3365            border,
3366            format,
3367            type_,
3368        );
3369
3370        let TexImage2DValidatorResult {
3371            texture,
3372            target,
3373            width,
3374            height,
3375            level,
3376            border,
3377            internal_format,
3378            format,
3379            data_type,
3380        } = match validator.validate() {
3381            Ok(result) => result,
3382            Err(_) => return Ok(()),
3383        };
3384
3385        self.base.tex_image_2d(
3386            &texture,
3387            target,
3388            data_type,
3389            internal_format,
3390            format,
3391            level,
3392            border,
3393            unpacking_alignment,
3394            Size2D::new(width, height),
3395            TexSource::BufferOffset(pbo_offset),
3396        );
3397
3398        Ok(())
3399    }
3400
3401    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.6>
3402    fn TexImage2D___(
3403        &self,
3404        target: u32,
3405        level: i32,
3406        internalformat: i32,
3407        width: i32,
3408        height: i32,
3409        border: i32,
3410        format: u32,
3411        type_: u32,
3412        source: TexImageSource,
3413    ) -> Fallible<()> {
3414        if self.bound_pixel_unpack_buffer.get().is_some() {
3415            self.base.webgl_error(InvalidOperation);
3416            return Ok(());
3417        }
3418
3419        let validator = TexImage2DValidator::new(
3420            &self.base,
3421            target,
3422            level,
3423            internalformat as u32,
3424            width,
3425            height,
3426            border,
3427            format,
3428            type_,
3429        );
3430
3431        let TexImage2DValidatorResult {
3432            texture,
3433            target,
3434            width: _,
3435            height: _,
3436            level,
3437            border,
3438            internal_format,
3439            format,
3440            data_type,
3441        } = match validator.validate() {
3442            Ok(result) => result,
3443            Err(_) => return Ok(()),
3444        };
3445
3446        let unpacking_alignment = self.base.texture_unpacking_alignment();
3447
3448        let pixels = match self.base.get_image_pixels(source)? {
3449            Some(pixels) => pixels,
3450            None => return Ok(()),
3451        };
3452
3453        self.base.tex_image_2d(
3454            &texture,
3455            target,
3456            data_type,
3457            internal_format,
3458            format,
3459            level,
3460            border,
3461            unpacking_alignment,
3462            pixels.size(),
3463            TexSource::Pixels(pixels),
3464        );
3465
3466        Ok(())
3467    }
3468
3469    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.6>
3470    #[expect(unsafe_code)]
3471    fn TexImage2D____(
3472        &self,
3473        target: u32,
3474        level: i32,
3475        internalformat: i32,
3476        width: i32,
3477        height: i32,
3478        border: i32,
3479        format: u32,
3480        type_: u32,
3481        src_data: CustomAutoRooterGuard<ArrayBufferView>,
3482        src_offset: u32,
3483    ) -> Fallible<()> {
3484        if self.bound_pixel_unpack_buffer.get().is_some() {
3485            self.base.webgl_error(InvalidOperation);
3486            return Ok(());
3487        }
3488
3489        if type_ == constants::FLOAT_32_UNSIGNED_INT_24_8_REV {
3490            self.base.webgl_error(InvalidOperation);
3491            return Ok(());
3492        }
3493
3494        let validator = TexImage2DValidator::new(
3495            &self.base,
3496            target,
3497            level,
3498            internalformat as u32,
3499            width,
3500            height,
3501            border,
3502            format,
3503            type_,
3504        );
3505
3506        let TexImage2DValidatorResult {
3507            texture,
3508            target,
3509            width,
3510            height,
3511            level,
3512            border,
3513            internal_format,
3514            format,
3515            data_type,
3516        } = match validator.validate() {
3517            Ok(result) => result,
3518            Err(_) => return Ok(()),
3519        };
3520
3521        let unpacking_alignment = self.base.texture_unpacking_alignment();
3522
3523        let src_elem_size = src_data.get_array_type().byte_size().unwrap();
3524        let src_byte_offset = src_offset as usize * src_elem_size;
3525
3526        if src_data.len() <= src_byte_offset {
3527            self.base.webgl_error(InvalidOperation);
3528            return Ok(());
3529        }
3530
3531        let buff =
3532            GenericSharedMemory::from_bytes(unsafe { &src_data.as_slice()[src_byte_offset..] });
3533
3534        let expected_byte_length = match self.base.validate_tex_image_2d_data(
3535            width,
3536            height,
3537            format,
3538            data_type,
3539            unpacking_alignment,
3540            Some(&*src_data),
3541        ) {
3542            Ok(byte_length) => byte_length,
3543            Err(()) => return Ok(()),
3544        };
3545
3546        if expected_byte_length as usize > buff.len() {
3547            self.base.webgl_error(InvalidOperation);
3548            return Ok(());
3549        }
3550
3551        let size = Size2D::new(width, height);
3552
3553        let (alpha_treatment, y_axis_treatment) =
3554            self.base.get_current_unpack_state(Alpha::NotPremultiplied);
3555
3556        self.base.tex_image_2d(
3557            &texture,
3558            target,
3559            data_type,
3560            internal_format,
3561            format,
3562            level,
3563            border,
3564            unpacking_alignment,
3565            size,
3566            TexSource::Pixels(TexPixels::from_array(
3567                buff,
3568                size,
3569                alpha_treatment,
3570                y_axis_treatment,
3571            )),
3572        );
3573
3574        Ok(())
3575    }
3576
3577    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
3578    fn TexSubImage2D(
3579        &self,
3580        target: u32,
3581        level: i32,
3582        xoffset: i32,
3583        yoffset: i32,
3584        width: i32,
3585        height: i32,
3586        format: u32,
3587        data_type: u32,
3588        pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
3589    ) -> Fallible<()> {
3590        self.base.TexSubImage2D(
3591            target, level, xoffset, yoffset, width, height, format, data_type, pixels,
3592        )
3593    }
3594
3595    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
3596    fn TexSubImage2D_(
3597        &self,
3598        target: u32,
3599        level: i32,
3600        xoffset: i32,
3601        yoffset: i32,
3602        format: u32,
3603        data_type: u32,
3604        source: TexImageSource,
3605    ) -> ErrorResult {
3606        self.base
3607            .TexSubImage2D_(target, level, xoffset, yoffset, format, data_type, source)
3608    }
3609
3610    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
3611    fn TexParameterf(&self, target: u32, name: u32, value: f32) {
3612        self.base.TexParameterf(target, name, value)
3613    }
3614
3615    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
3616    fn TexParameteri(&self, target: u32, name: u32, value: i32) {
3617        self.base.TexParameteri(target, name, value)
3618    }
3619
3620    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
3621    fn CheckFramebufferStatus(&self, target: u32) -> u32 {
3622        let fb_slot = match target {
3623            constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
3624                self.base.get_draw_framebuffer_slot()
3625            },
3626            constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
3627            _ => {
3628                self.base.webgl_error(InvalidEnum);
3629                return 0;
3630            },
3631        };
3632        match fb_slot.get() {
3633            Some(fb) => fb.check_status(),
3634            None => constants::FRAMEBUFFER_COMPLETE,
3635        }
3636    }
3637
3638    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
3639    fn RenderbufferStorage(&self, target: u32, internal_format: u32, width: i32, height: i32) {
3640        self.base
3641            .RenderbufferStorage(target, internal_format, width, height)
3642    }
3643
3644    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.4S>
3645    fn BlitFramebuffer(
3646        &self,
3647        src_x0: i32,
3648        src_y0: i32,
3649        src_x1: i32,
3650        src_y1: i32,
3651        dst_x0: i32,
3652        dst_y0: i32,
3653        dst_x1: i32,
3654        dst_y1: i32,
3655        mask: u32,
3656        filter: u32,
3657    ) {
3658        bitflags! {
3659            struct BlitFrameBufferFlags: u32 {
3660                const DEPTH = constants::DEPTH_BUFFER_BIT;
3661                const COLOR = constants::COLOR_BUFFER_BIT;
3662                const STENCIL = constants::STENCIL_BUFFER_BIT;
3663                const DEPTH_STENCIL = constants::DEPTH_BUFFER_BIT | constants::STENCIL_BUFFER_BIT;
3664            }
3665        };
3666        let Some(bits) = BlitFrameBufferFlags::from_bits(mask) else {
3667            return self.base.webgl_error(InvalidValue);
3668        };
3669        let attributes = self.base.GetContextAttributes().unwrap();
3670
3671        if bits.intersects(BlitFrameBufferFlags::DEPTH_STENCIL) {
3672            match filter {
3673                constants::LINEAR => return self.base.webgl_error(InvalidOperation),
3674                constants::NEAREST => {},
3675                _ => return self.base.webgl_error(InvalidOperation),
3676            }
3677        }
3678
3679        let src_fb = self.base.get_read_framebuffer_slot().get();
3680        let dst_fb = self.base.get_draw_framebuffer_slot().get();
3681
3682        let get_default_formats = || -> WebGLResult<(Option<u32>, Option<u32>, Option<u32>)> {
3683            // All attempts to blit to an antialiased back buffer should fail.
3684            if attributes.antialias {
3685                return Err(InvalidOperation);
3686            };
3687            let color = if attributes.alpha {
3688                Some(constants::RGBA8)
3689            } else {
3690                Some(constants::RGB8)
3691            };
3692            let (depth, stencil) = match (attributes.depth, attributes.stencil) {
3693                (true, true) => (
3694                    Some(constants::DEPTH24_STENCIL8),
3695                    Some(constants::DEPTH24_STENCIL8),
3696                ),
3697                (true, false) => (Some(constants::DEPTH_COMPONENT16), None),
3698                (false, true) => (None, Some(constants::STENCIL_INDEX8)),
3699                _ => (None, None),
3700            };
3701            Ok((color, depth, stencil))
3702        };
3703
3704        let (src_color, src_depth, src_stencil) = match src_fb {
3705            Some(fb) => {
3706                handle_potential_webgl_error!(self.base, fb.get_attachment_formats(), return)
3707            },
3708            None => handle_potential_webgl_error!(self.base, get_default_formats(), return),
3709        };
3710        let (dst_color, dst_depth, dst_stencil) = match dst_fb {
3711            Some(fb) => {
3712                handle_potential_webgl_error!(self.base, fb.get_attachment_formats(), return)
3713            },
3714            None => handle_potential_webgl_error!(self.base, get_default_formats(), return),
3715        };
3716
3717        if bits.intersects(BlitFrameBufferFlags::COLOR) && src_color != dst_color {
3718            return self.base.webgl_error(InvalidOperation);
3719        }
3720        if bits.intersects(BlitFrameBufferFlags::DEPTH) && src_depth != dst_depth {
3721            return self.base.webgl_error(InvalidOperation);
3722        }
3723        if bits.intersects(BlitFrameBufferFlags::STENCIL) && src_stencil != dst_stencil {
3724            return self.base.webgl_error(InvalidOperation);
3725        }
3726
3727        let src_width = src_x1.checked_sub(src_x0);
3728        let dst_width = dst_x1.checked_sub(dst_x0);
3729        let src_height = src_y1.checked_sub(src_y0);
3730        let dst_height = dst_y1.checked_sub(dst_y0);
3731
3732        if src_width.is_none() ||
3733            dst_width.is_none() ||
3734            src_height.is_none() ||
3735            dst_height.is_none()
3736        {
3737            return self.base.webgl_error(InvalidOperation);
3738        }
3739
3740        self.base.send_command(WebGLCommand::BlitFrameBuffer(
3741            src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter,
3742        ));
3743    }
3744
3745    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
3746    fn FramebufferRenderbuffer(
3747        &self,
3748        target: u32,
3749        attachment: u32,
3750        renderbuffertarget: u32,
3751        rb: Option<&WebGLRenderbuffer>,
3752    ) {
3753        if let Some(rb) = rb {
3754            handle_potential_webgl_error!(self.base, self.base.validate_ownership(rb), return);
3755        }
3756
3757        let fb_slot = match target {
3758            constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
3759                self.base.get_draw_framebuffer_slot()
3760            },
3761            constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
3762            _ => return self.base.webgl_error(InvalidEnum),
3763        };
3764
3765        if renderbuffertarget != constants::RENDERBUFFER {
3766            return self.base.webgl_error(InvalidEnum);
3767        }
3768
3769        match fb_slot.get() {
3770            Some(fb) => match attachment {
3771                constants::DEPTH_STENCIL_ATTACHMENT => {
3772                    handle_potential_webgl_error!(
3773                        self.base,
3774                        fb.renderbuffer(constants::DEPTH_ATTACHMENT, rb)
3775                    );
3776                    handle_potential_webgl_error!(
3777                        self.base,
3778                        fb.renderbuffer(constants::STENCIL_ATTACHMENT, rb)
3779                    );
3780                },
3781                _ => handle_potential_webgl_error!(self.base, fb.renderbuffer(attachment, rb)),
3782            },
3783            None => self.base.webgl_error(InvalidOperation),
3784        };
3785    }
3786
3787    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
3788    fn FramebufferTexture2D(
3789        &self,
3790        target: u32,
3791        attachment: u32,
3792        textarget: u32,
3793        texture: Option<&WebGLTexture>,
3794        level: i32,
3795    ) {
3796        if let Some(texture) = texture {
3797            handle_potential_webgl_error!(self.base, self.base.validate_ownership(texture), return);
3798        }
3799
3800        let fb_slot = match target {
3801            constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
3802                self.base.get_draw_framebuffer_slot()
3803            },
3804            constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
3805            _ => return self.base.webgl_error(InvalidEnum),
3806        };
3807        match fb_slot.get() {
3808            Some(fb) => handle_potential_webgl_error!(
3809                self.base,
3810                fb.texture2d(attachment, textarget, texture, level)
3811            ),
3812            None => self.base.webgl_error(InvalidOperation),
3813        }
3814    }
3815
3816    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
3817    fn GetAttachedShaders(&self, program: &WebGLProgram) -> Option<Vec<DomRoot<WebGLShader>>> {
3818        self.base.GetAttachedShaders(program)
3819    }
3820
3821    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.9>
3822    fn DrawArraysInstanced(
3823        &self,
3824        cx: &mut JSContext,
3825        mode: u32,
3826        first: i32,
3827        count: i32,
3828        primcount: i32,
3829    ) {
3830        self.validate_uniform_block_for_draw();
3831        self.validate_vertex_attribs_for_draw(cx);
3832        handle_potential_webgl_error!(
3833            self.base,
3834            self.base
3835                .draw_arrays_instanced(cx, mode, first, count, primcount)
3836        )
3837    }
3838
3839    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.9>
3840    fn DrawElementsInstanced(
3841        &self,
3842        cx: &mut JSContext,
3843        mode: u32,
3844        count: i32,
3845        type_: u32,
3846        offset: i64,
3847        primcount: i32,
3848    ) {
3849        self.validate_uniform_block_for_draw();
3850        self.validate_vertex_attribs_for_draw(cx);
3851        handle_potential_webgl_error!(
3852            self.base,
3853            self.base
3854                .draw_elements_instanced(cx, mode, count, type_, offset, primcount)
3855        )
3856    }
3857
3858    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.9>
3859    fn DrawRangeElements(
3860        &self,
3861        cx: &mut JSContext,
3862        mode: u32,
3863        start: u32,
3864        end: u32,
3865        count: i32,
3866        type_: u32,
3867        offset: i64,
3868    ) {
3869        if end < start {
3870            self.base.webgl_error(InvalidValue);
3871            return;
3872        }
3873        self.validate_uniform_block_for_draw();
3874        self.validate_vertex_attribs_for_draw(cx);
3875        handle_potential_webgl_error!(
3876            self.base,
3877            self.base
3878                .draw_elements_instanced(cx, mode, count, type_, offset, 1)
3879        )
3880    }
3881
3882    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.9>
3883    fn VertexAttribDivisor(&self, cx: &mut JSContext, index: u32, divisor: u32) {
3884        self.base.vertex_attrib_divisor(cx, index, divisor);
3885    }
3886
3887    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.12>
3888    fn CreateQuery(&self, cx: &mut JSContext) -> Option<DomRoot<WebGLQuery>> {
3889        Some(WebGLQuery::new(cx, &self.base))
3890    }
3891
3892    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.12>
3893    fn DeleteQuery(&self, query: Option<&WebGLQuery>) {
3894        if let Some(query) = query {
3895            handle_potential_webgl_error!(self.base, self.base.validate_ownership(query), return);
3896
3897            if let Some(query_target) = query.target() {
3898                let slot = match query_target {
3899                    constants::ANY_SAMPLES_PASSED | constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
3900                        &self.occlusion_query
3901                    },
3902                    constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => &self.primitives_query,
3903                    _ => unreachable!(),
3904                };
3905                if let Some(stored_query) = slot.get() &&
3906                    stored_query.target() == query.target()
3907                {
3908                    slot.set(None);
3909                }
3910            }
3911
3912            query.delete(Operation::Infallible);
3913        }
3914    }
3915
3916    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.12>
3917    fn IsQuery(&self, query: Option<&WebGLQuery>) -> bool {
3918        match query {
3919            Some(query) => self.base.validate_ownership(query).is_ok() && query.is_valid(),
3920            None => false,
3921        }
3922    }
3923
3924    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.13>
3925    fn CreateSampler(&self, cx: &mut JSContext) -> Option<DomRoot<WebGLSampler>> {
3926        Some(WebGLSampler::new(cx, &self.base))
3927    }
3928
3929    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.13>
3930    fn DeleteSampler(&self, sampler: Option<&WebGLSampler>) {
3931        if let Some(sampler) = sampler {
3932            handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
3933            for slot in self.samplers.iter() {
3934                if slot.get().is_some_and(|s| sampler == &*s) {
3935                    slot.set(None);
3936                }
3937            }
3938            sampler.delete(Operation::Infallible);
3939        }
3940    }
3941
3942    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.13>
3943    fn IsSampler(&self, sampler: Option<&WebGLSampler>) -> bool {
3944        match sampler {
3945            Some(sampler) => self.base.validate_ownership(sampler).is_ok() && sampler.is_valid(),
3946            None => false,
3947        }
3948    }
3949
3950    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.12>
3951    fn BeginQuery(&self, target: u32, query: &WebGLQuery) {
3952        handle_potential_webgl_error!(self.base, self.base.validate_ownership(query), return);
3953
3954        let active_query = match target {
3955            constants::ANY_SAMPLES_PASSED | constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
3956                &self.occlusion_query
3957            },
3958            constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => &self.primitives_query,
3959            _ => {
3960                self.base.webgl_error(InvalidEnum);
3961                return;
3962            },
3963        };
3964        if active_query.get().is_some() {
3965            self.base.webgl_error(InvalidOperation);
3966            return;
3967        }
3968        let result = query.begin(&self.base, target);
3969        match result {
3970            Ok(_) => active_query.set(Some(query)),
3971            Err(error) => self.base.webgl_error(error),
3972        }
3973    }
3974
3975    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.12>
3976    fn EndQuery(&self, target: u32) {
3977        let active_query = match target {
3978            constants::ANY_SAMPLES_PASSED | constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
3979                self.occlusion_query.take()
3980            },
3981            constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => self.primitives_query.take(),
3982            _ => {
3983                self.base.webgl_error(InvalidEnum);
3984                return;
3985            },
3986        };
3987        match active_query {
3988            None => self.base.webgl_error(InvalidOperation),
3989            Some(query) => {
3990                let result = query.end(&self.base, target);
3991                if let Err(error) = result {
3992                    self.base.webgl_error(error);
3993                }
3994            },
3995        }
3996    }
3997
3998    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.12>
3999    fn GetQuery(&self, target: u32, pname: u32) -> Option<DomRoot<WebGLQuery>> {
4000        if pname != constants::CURRENT_QUERY {
4001            self.base.webgl_error(InvalidEnum);
4002            return None;
4003        }
4004        let active_query = match target {
4005            constants::ANY_SAMPLES_PASSED | constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
4006                self.occlusion_query.get()
4007            },
4008            constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => self.primitives_query.get(),
4009            _ => {
4010                self.base.webgl_error(InvalidEnum);
4011                None
4012            },
4013        };
4014        if let Some(query) = active_query.as_ref() &&
4015            query.target() != Some(target)
4016        {
4017            return None;
4018        }
4019        active_query
4020    }
4021
4022    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.12>
4023    fn GetQueryParameter(
4024        &self,
4025        _cx: &mut JSContext,
4026        query: &WebGLQuery,
4027        pname: u32,
4028        mut retval: MutableHandleValue,
4029    ) {
4030        handle_potential_webgl_error!(
4031            self.base,
4032            self.base.validate_ownership(query),
4033            return retval.set(NullValue())
4034        );
4035        match query.get_parameter(&self.base, pname) {
4036            Ok(value) => match pname {
4037                constants::QUERY_RESULT => retval.set(UInt32Value(value)),
4038                constants::QUERY_RESULT_AVAILABLE => retval.set(BooleanValue(value != 0)),
4039                _ => unreachable!(),
4040            },
4041            Err(error) => {
4042                self.base.webgl_error(error);
4043                retval.set(NullValue())
4044            },
4045        }
4046    }
4047
4048    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.14>
4049    fn FenceSync(
4050        &self,
4051        cx: &mut JSContext,
4052        condition: u32,
4053        flags: u32,
4054    ) -> Option<DomRoot<WebGLSync>> {
4055        if flags != 0 {
4056            self.base.webgl_error(InvalidValue);
4057            return None;
4058        }
4059        if condition != constants::SYNC_GPU_COMMANDS_COMPLETE {
4060            self.base.webgl_error(InvalidEnum);
4061            return None;
4062        }
4063
4064        Some(WebGLSync::new(cx, &self.base))
4065    }
4066
4067    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.14>
4068    fn IsSync(&self, sync: Option<&WebGLSync>) -> bool {
4069        match sync {
4070            Some(sync) => {
4071                // Returns false if the sync was generated by a different
4072                // WebGL2RenderingContext than this one.
4073                if self.base.validate_ownership(sync).is_err() {
4074                    return false;
4075                }
4076
4077                // Returns false if the sync's invalidated flag is set.
4078                if !sync.is_valid() {
4079                    return false;
4080                }
4081
4082                // Return true if the passed WebGLSync is valid and false otherwise.
4083                handle_potential_webgl_error!(
4084                    self.base,
4085                    self.base.validate_ownership(sync),
4086                    return false
4087                );
4088                let (sender, receiver) = webgl_channel().unwrap();
4089                self.base
4090                    .send_command(WebGLCommand::IsSync(sync.id(), sender));
4091                receiver.recv().unwrap()
4092            },
4093            None => false,
4094        }
4095    }
4096
4097    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.14>
4098    fn ClientWaitSync(&self, sync: &WebGLSync, flags: u32, timeout: u64) -> u32 {
4099        if !sync.is_valid() {
4100            self.base.webgl_error(InvalidOperation);
4101            return constants::WAIT_FAILED;
4102        }
4103        handle_potential_webgl_error!(
4104            self.base,
4105            self.base.validate_ownership(sync),
4106            return constants::WAIT_FAILED
4107        );
4108        if flags != 0 && flags != constants::SYNC_FLUSH_COMMANDS_BIT {
4109            self.base.webgl_error(InvalidValue);
4110            return constants::WAIT_FAILED;
4111        }
4112        if timeout > self.base.limits().max_client_wait_timeout_webgl.as_nanos() as u64 {
4113            self.base.webgl_error(InvalidOperation);
4114            return constants::WAIT_FAILED;
4115        }
4116
4117        match sync.client_wait_sync(&self.base, flags, timeout) {
4118            Some(status) => status,
4119            None => constants::WAIT_FAILED,
4120        }
4121    }
4122
4123    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.14>
4124    fn WaitSync(&self, sync: &WebGLSync, flags: u32, timeout: i64) {
4125        if !sync.is_valid() {
4126            self.base.webgl_error(InvalidOperation);
4127            return;
4128        }
4129        handle_potential_webgl_error!(self.base, self.base.validate_ownership(sync), return);
4130        if flags != 0 {
4131            self.base.webgl_error(InvalidValue);
4132            return;
4133        }
4134        if timeout != constants::TIMEOUT_IGNORED {
4135            self.base.webgl_error(InvalidValue);
4136            return;
4137        }
4138
4139        self.base
4140            .send_command(WebGLCommand::WaitSync(sync.id(), flags, timeout));
4141    }
4142
4143    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.14>
4144    fn GetSyncParameter(
4145        &self,
4146        _cx: &mut JSContext,
4147        sync: &WebGLSync,
4148        pname: u32,
4149        mut retval: MutableHandleValue,
4150    ) {
4151        if !sync.is_valid() {
4152            self.base.webgl_error(InvalidOperation);
4153            return retval.set(NullValue());
4154        }
4155        handle_potential_webgl_error!(
4156            self.base,
4157            self.base.validate_ownership(sync),
4158            return retval.set(NullValue())
4159        );
4160        match pname {
4161            constants::OBJECT_TYPE | constants::SYNC_CONDITION | constants::SYNC_FLAGS => {
4162                let (sender, receiver) = webgl_channel().unwrap();
4163                self.base
4164                    .send_command(WebGLCommand::GetSyncParameter(sync.id(), pname, sender));
4165                retval.set(UInt32Value(receiver.recv().unwrap()))
4166            },
4167            constants::SYNC_STATUS => match sync.get_sync_status(pname, &self.base) {
4168                Some(status) => retval.set(UInt32Value(status)),
4169                None => retval.set(UInt32Value(constants::UNSIGNALED)),
4170            },
4171            _ => {
4172                self.base.webgl_error(InvalidEnum);
4173                retval.set(NullValue())
4174            },
4175        }
4176    }
4177
4178    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.14>
4179    fn DeleteSync(&self, sync: Option<&WebGLSync>) {
4180        if let Some(sync) = sync {
4181            handle_potential_webgl_error!(self.base, self.base.validate_ownership(sync), return);
4182            sync.delete(Operation::Infallible);
4183        }
4184    }
4185
4186    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.13>
4187    fn BindSampler(&self, unit: u32, sampler: Option<&WebGLSampler>) {
4188        if let Some(sampler) = sampler {
4189            handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
4190
4191            if unit as usize >= self.samplers.len() {
4192                self.base.webgl_error(InvalidValue);
4193                return;
4194            }
4195
4196            let result = sampler.bind(&self.base, unit);
4197            match result {
4198                Ok(_) => self.samplers[unit as usize].set(Some(sampler)),
4199                Err(error) => self.base.webgl_error(error),
4200            }
4201        }
4202    }
4203
4204    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.17>
4205    fn BindVertexArray(&self, array: Option<&WebGLVertexArrayObject>) {
4206        self.base.bind_vertex_array_webgl2(array);
4207    }
4208
4209    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.13>
4210    fn SamplerParameteri(&self, sampler: &WebGLSampler, pname: u32, param: i32) {
4211        handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
4212        let param = WebGLSamplerValue::GLenum(param as u32);
4213        let result = sampler.set_parameter(&self.base, pname, param);
4214        if let Err(error) = result {
4215            self.base.webgl_error(error);
4216        }
4217    }
4218
4219    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.13>
4220    fn SamplerParameterf(&self, sampler: &WebGLSampler, pname: u32, param: f32) {
4221        handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
4222        let param = WebGLSamplerValue::Float(param);
4223        let result = sampler.set_parameter(&self.base, pname, param);
4224        if let Err(error) = result {
4225            self.base.webgl_error(error);
4226        }
4227    }
4228
4229    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.13>
4230    fn GetSamplerParameter(
4231        &self,
4232        _cx: &mut JSContext,
4233        sampler: &WebGLSampler,
4234        pname: u32,
4235        mut retval: MutableHandleValue,
4236    ) {
4237        handle_potential_webgl_error!(
4238            self.base,
4239            self.base.validate_ownership(sampler),
4240            return retval.set(NullValue())
4241        );
4242        match sampler.get_parameter(&self.base, pname) {
4243            Ok(value) => match value {
4244                WebGLSamplerValue::GLenum(value) => retval.set(UInt32Value(value)),
4245                WebGLSamplerValue::Float(value) => retval.set(DoubleValue(value as f64)),
4246            },
4247            Err(error) => {
4248                self.base.webgl_error(error);
4249                retval.set(NullValue())
4250            },
4251        }
4252    }
4253
4254    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.15>
4255    fn CreateTransformFeedback(
4256        &self,
4257        cx: &mut JSContext,
4258    ) -> Option<DomRoot<WebGLTransformFeedback>> {
4259        Some(WebGLTransformFeedback::new(cx, &self.base))
4260    }
4261
4262    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.15>
4263    fn DeleteTransformFeedback(&self, tf: Option<&WebGLTransformFeedback>) {
4264        if let Some(tf) = tf {
4265            handle_potential_webgl_error!(self.base, self.base.validate_ownership(tf), return);
4266            if tf.is_active() {
4267                self.base.webgl_error(InvalidOperation);
4268                return;
4269            }
4270            tf.delete(Operation::Infallible);
4271            self.current_transform_feedback.set(None);
4272        }
4273    }
4274
4275    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.15>
4276    fn IsTransformFeedback(&self, tf: Option<&WebGLTransformFeedback>) -> bool {
4277        match tf {
4278            Some(tf) => {
4279                // Returns false if the transform feedback was generated by a different
4280                // WebGL2RenderingContext than this one.
4281                if self.base.validate_ownership(tf).is_err() {
4282                    return false;
4283                }
4284
4285                // Returns false if the transform feedback's invalidated flag is set.
4286                if !tf.is_valid() {
4287                    return false;
4288                }
4289
4290                // Return true if the passed WebGLTransformFeedback is valid and false otherwise.
4291                handle_potential_webgl_error!(
4292                    self.base,
4293                    self.base.validate_ownership(tf),
4294                    return false
4295                );
4296                let (sender, receiver) = webgl_channel().unwrap();
4297                self.base
4298                    .send_command(WebGLCommand::IsTransformFeedback(tf.id(), sender));
4299                receiver.recv().unwrap()
4300            },
4301            // Returns false if `transformFeedback` is null.
4302            None => false,
4303        }
4304    }
4305
4306    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.15>
4307    fn BindTransformFeedback(&self, target: u32, tf: Option<&WebGLTransformFeedback>) {
4308        if target != constants::TRANSFORM_FEEDBACK {
4309            self.base.webgl_error(InvalidEnum);
4310            return;
4311        }
4312        match tf {
4313            Some(transform_feedback) => {
4314                handle_potential_webgl_error!(
4315                    self.base,
4316                    self.base.validate_ownership(transform_feedback),
4317                    return
4318                );
4319                if !transform_feedback.is_valid() {
4320                    self.base.webgl_error(InvalidOperation);
4321                    return;
4322                }
4323                if let Some(current_tf) = self.current_transform_feedback.get() &&
4324                    current_tf.is_active() &&
4325                    !current_tf.is_paused()
4326                {
4327                    self.base.webgl_error(InvalidOperation);
4328                    return;
4329                }
4330                transform_feedback.bind(&self.base, target);
4331                self.current_transform_feedback
4332                    .set(Some(transform_feedback));
4333            },
4334            None => self
4335                .base
4336                .send_command(WebGLCommand::BindTransformFeedback(target, 0)),
4337        }
4338    }
4339
4340    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.15>
4341    #[expect(non_snake_case)]
4342    fn BeginTransformFeedback(&self, primitiveMode: u32) {
4343        match primitiveMode {
4344            constants::POINTS | constants::LINES | constants::TRIANGLES => {},
4345            _ => {
4346                self.base.webgl_error(InvalidEnum);
4347                return;
4348            },
4349        };
4350        let current_tf = match self.current_transform_feedback.get() {
4351            Some(current_tf) => current_tf,
4352            None => {
4353                self.base.webgl_error(InvalidOperation);
4354                return;
4355            },
4356        };
4357        if current_tf.is_active() {
4358            self.base.webgl_error(InvalidOperation);
4359            return;
4360        };
4361        let program = match self.base.current_program() {
4362            Some(program) => program,
4363            None => {
4364                self.base.webgl_error(InvalidOperation);
4365                return;
4366            },
4367        };
4368        if !program.is_linked() || program.transform_feedback_varyings_length() == 0 {
4369            self.base.webgl_error(InvalidOperation);
4370            return;
4371        };
4372        current_tf.begin(&self.base, primitiveMode);
4373    }
4374
4375    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.15>
4376    fn EndTransformFeedback(&self) {
4377        if let Some(current_tf) = self.current_transform_feedback.get() {
4378            if !current_tf.is_active() {
4379                self.base.webgl_error(InvalidOperation);
4380                return;
4381            }
4382            current_tf.end(&self.base);
4383        }
4384    }
4385
4386    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.15>
4387    fn ResumeTransformFeedback(&self) {
4388        if let Some(current_tf) = self.current_transform_feedback.get() {
4389            if !current_tf.is_active() || !current_tf.is_paused() {
4390                self.base.webgl_error(InvalidOperation);
4391                return;
4392            }
4393            current_tf.resume(&self.base);
4394        }
4395    }
4396
4397    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.15>
4398    fn PauseTransformFeedback(&self) {
4399        if let Some(current_tf) = self.current_transform_feedback.get() {
4400            if !current_tf.is_active() || current_tf.is_paused() {
4401                self.base.webgl_error(InvalidOperation);
4402                return;
4403            }
4404            current_tf.pause(&self.base);
4405        }
4406    }
4407
4408    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.15>
4409    #[expect(non_snake_case)]
4410    fn TransformFeedbackVaryings(
4411        &self,
4412        program: &WebGLProgram,
4413        varyings: Vec<DOMString>,
4414        bufferMode: u32,
4415    ) {
4416        // Spec: If program was generated by a different WebGL2RenderingContext
4417        // than this one, generates an INVALID_OPERATION error.
4418        if let Err(error) = self.base.validate_ownership(program) {
4419            self.base.webgl_error(error);
4420            return;
4421        }
4422        handle_potential_webgl_error!(self.base, program.validate(), return);
4423        let strs = varyings
4424            .iter()
4425            .map(|name| String::from(name.to_owned()))
4426            .collect::<Vec<String>>();
4427        match bufferMode {
4428            constants::INTERLEAVED_ATTRIBS => {
4429                self.base
4430                    .send_command(WebGLCommand::TransformFeedbackVaryings(
4431                        program.id(),
4432                        strs,
4433                        bufferMode,
4434                    ));
4435            },
4436            constants::SEPARATE_ATTRIBS => {
4437                let max_tf_sp_att =
4438                    self.base.limits().max_transform_feedback_separate_attribs as usize;
4439                if strs.len() >= max_tf_sp_att {
4440                    self.base.webgl_error(InvalidValue);
4441                    return;
4442                }
4443                self.base
4444                    .send_command(WebGLCommand::TransformFeedbackVaryings(
4445                        program.id(),
4446                        strs,
4447                        bufferMode,
4448                    ));
4449            },
4450            _ => self.base.webgl_error(InvalidEnum),
4451        }
4452    }
4453
4454    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.15>
4455    fn GetTransformFeedbackVarying(
4456        &self,
4457        cx: &mut JSContext,
4458        program: &WebGLProgram,
4459        index: u32,
4460    ) -> Option<DomRoot<WebGLActiveInfo>> {
4461        // Spec: If program was generated by a different WebGL2RenderingContext,
4462        // generate INVALID_OPERATION and return null.
4463        if let Err(error) = self.base.validate_ownership(program) {
4464            self.base.webgl_error(error);
4465            return None;
4466        }
4467
4468        handle_potential_webgl_error!(self.base, program.validate(), return None);
4469
4470        if index >= program.transform_feedback_varyings_length() as u32 {
4471            self.base.webgl_error(InvalidValue);
4472            return None;
4473        }
4474
4475        let (sender, receiver) = webgl_channel().unwrap();
4476        self.base
4477            .send_command(WebGLCommand::GetTransformFeedbackVarying(
4478                program.id(),
4479                index,
4480                sender,
4481            ));
4482        let (size, ty, name) = receiver.recv().unwrap();
4483        Some(WebGLActiveInfo::new(
4484            cx,
4485            self.base.global().as_window(),
4486            size,
4487            ty,
4488            DOMString::from(name),
4489        ))
4490    }
4491
4492    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.16>
4493    fn BindBufferBase(&self, target: u32, index: u32, buffer: Option<&WebGLBuffer>) {
4494        let (generic_slot, indexed_bindings) = match target {
4495            constants::TRANSFORM_FEEDBACK_BUFFER => (
4496                &self.bound_transform_feedback_buffer,
4497                &self.indexed_transform_feedback_buffer_bindings,
4498            ),
4499            constants::UNIFORM_BUFFER => (
4500                &self.bound_uniform_buffer,
4501                &self.indexed_uniform_buffer_bindings,
4502            ),
4503            _ => return self.base.webgl_error(InvalidEnum),
4504        };
4505        let indexed_binding = match indexed_bindings.get(index as usize) {
4506            Some(slot) => slot,
4507            None => return self.base.webgl_error(InvalidValue),
4508        };
4509
4510        if let Some(buffer) = buffer {
4511            handle_potential_webgl_error!(self.base, self.base.validate_ownership(buffer), return);
4512
4513            if buffer.is_marked_for_deletion() {
4514                return self.base.webgl_error(InvalidOperation);
4515            }
4516            handle_potential_webgl_error!(self.base, buffer.set_target_maybe(target), return);
4517
4518            // for both the generic and the indexed bindings
4519            buffer.increment_attached_counter();
4520            buffer.increment_attached_counter();
4521        }
4522
4523        self.base.send_command(WebGLCommand::BindBufferBase(
4524            target,
4525            index,
4526            buffer.map(|b| b.id()),
4527        ));
4528
4529        for slot in &[generic_slot, &indexed_binding.buffer] {
4530            if let Some(old) = slot.get() {
4531                old.decrement_attached_counter(Operation::Infallible);
4532            }
4533            slot.set(buffer);
4534        }
4535        indexed_binding.start.set(0);
4536        indexed_binding.size.set(0);
4537    }
4538
4539    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.16>
4540    fn BindBufferRange(
4541        &self,
4542        target: u32,
4543        index: u32,
4544        buffer: Option<&WebGLBuffer>,
4545        offset: i64,
4546        size: i64,
4547    ) {
4548        let (generic_slot, indexed_bindings) = match target {
4549            constants::TRANSFORM_FEEDBACK_BUFFER => (
4550                &self.bound_transform_feedback_buffer,
4551                &self.indexed_transform_feedback_buffer_bindings,
4552            ),
4553            constants::UNIFORM_BUFFER => (
4554                &self.bound_uniform_buffer,
4555                &self.indexed_uniform_buffer_bindings,
4556            ),
4557            _ => return self.base.webgl_error(InvalidEnum),
4558        };
4559        let indexed_binding = match indexed_bindings.get(index as usize) {
4560            Some(slot) => slot,
4561            None => return self.base.webgl_error(InvalidValue),
4562        };
4563
4564        if offset < 0 || size < 0 {
4565            return self.base.webgl_error(InvalidValue);
4566        }
4567        if buffer.is_some() && size == 0 {
4568            return self.base.webgl_error(InvalidValue);
4569        }
4570
4571        match target {
4572            constants::TRANSFORM_FEEDBACK_BUFFER => {
4573                if size % 4 != 0 && offset % 4 != 0 {
4574                    return self.base.webgl_error(InvalidValue);
4575                }
4576            },
4577            constants::UNIFORM_BUFFER => {
4578                let offset_alignment = self.base.limits().uniform_buffer_offset_alignment;
4579                if offset % offset_alignment as i64 != 0 {
4580                    return self.base.webgl_error(InvalidValue);
4581                }
4582            },
4583            _ => unreachable!(),
4584        }
4585
4586        if let Some(buffer) = buffer {
4587            handle_potential_webgl_error!(self.base, self.base.validate_ownership(buffer), return);
4588
4589            if buffer.is_marked_for_deletion() {
4590                return self.base.webgl_error(InvalidOperation);
4591            }
4592            handle_potential_webgl_error!(self.base, buffer.set_target_maybe(target), return);
4593
4594            // for both the generic and the indexed bindings
4595            buffer.increment_attached_counter();
4596            buffer.increment_attached_counter();
4597        }
4598
4599        self.base.send_command(WebGLCommand::BindBufferRange(
4600            target,
4601            index,
4602            buffer.map(|b| b.id()),
4603            offset,
4604            size,
4605        ));
4606
4607        for slot in &[generic_slot, &indexed_binding.buffer] {
4608            if let Some(old) = slot.get() {
4609                old.decrement_attached_counter(Operation::Infallible);
4610            }
4611            slot.set(buffer);
4612        }
4613        indexed_binding.start.set(offset);
4614        indexed_binding.size.set(size);
4615    }
4616
4617    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.16>
4618    fn GetUniformIndices(&self, program: &WebGLProgram, names: Vec<DOMString>) -> Option<Vec<u32>> {
4619        handle_potential_webgl_error!(
4620            self.base,
4621            self.base.validate_ownership(program),
4622            return None
4623        );
4624        let indices = handle_potential_webgl_error!(
4625            self.base,
4626            program.get_uniform_indices(names),
4627            return None
4628        );
4629        Some(indices)
4630    }
4631
4632    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.16>
4633    fn GetActiveUniforms(
4634        &self,
4635        cx: &mut JSContext,
4636        program: &WebGLProgram,
4637        indices: Vec<u32>,
4638        pname: u32,
4639        mut rval: MutableHandleValue,
4640    ) {
4641        handle_potential_webgl_error!(
4642            self.base,
4643            self.base.validate_ownership(program),
4644            return rval.set(NullValue())
4645        );
4646        let values = handle_potential_webgl_error!(
4647            self.base,
4648            program.get_active_uniforms(indices, pname),
4649            return rval.set(NullValue())
4650        );
4651
4652        match pname {
4653            constants::UNIFORM_SIZE |
4654            constants::UNIFORM_TYPE |
4655            constants::UNIFORM_BLOCK_INDEX |
4656            constants::UNIFORM_OFFSET |
4657            constants::UNIFORM_ARRAY_STRIDE |
4658            constants::UNIFORM_MATRIX_STRIDE => {
4659                values.safe_to_jsval(cx, rval);
4660            },
4661            constants::UNIFORM_IS_ROW_MAJOR => {
4662                let values = values.iter().map(|&v| v != 0).collect::<Vec<_>>();
4663                values.safe_to_jsval(cx, rval);
4664            },
4665            _ => unreachable!(),
4666        }
4667    }
4668
4669    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.16>
4670    fn GetUniformBlockIndex(&self, program: &WebGLProgram, block_name: DOMString) -> u32 {
4671        handle_potential_webgl_error!(
4672            self.base,
4673            self.base.validate_ownership(program),
4674            return constants::INVALID_INDEX
4675        );
4676        handle_potential_webgl_error!(
4677            self.base,
4678            program.get_uniform_block_index(block_name),
4679            constants::INVALID_INDEX
4680        )
4681    }
4682
4683    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.16>
4684    #[expect(unsafe_code)]
4685    fn GetActiveUniformBlockParameter(
4686        &self,
4687        cx: &mut JSContext,
4688        program: &WebGLProgram,
4689        block_index: u32,
4690        pname: u32,
4691        mut retval: MutableHandleValue,
4692    ) {
4693        handle_potential_webgl_error!(
4694            self.base,
4695            self.base.validate_ownership(program),
4696            return retval.set(NullValue())
4697        );
4698        let values = handle_potential_webgl_error!(
4699            self.base,
4700            program.get_active_uniform_block_parameter(block_index, pname),
4701            return retval.set(NullValue())
4702        );
4703        match pname {
4704            constants::UNIFORM_BLOCK_BINDING |
4705            constants::UNIFORM_BLOCK_DATA_SIZE |
4706            constants::UNIFORM_BLOCK_ACTIVE_UNIFORMS => {
4707                assert!(values.len() == 1);
4708                retval.set(UInt32Value(values[0] as u32))
4709            },
4710            constants::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES => unsafe {
4711                let values = values.iter().map(|&v| v as u32).collect::<Vec<_>>();
4712                rooted!(&in(cx) let mut result = ptr::null_mut::<JSObject>());
4713                Uint32Array::create(cx.raw_cx(), CreateWith::Slice(&values), result.handle_mut())
4714                    .unwrap();
4715                retval.set(ObjectValue(result.get()))
4716            },
4717            constants::UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER |
4718            constants::UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER => {
4719                assert!(values.len() == 1);
4720                retval.set(BooleanValue(values[0] != 0))
4721            },
4722            _ => unreachable!(),
4723        }
4724    }
4725
4726    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.16>
4727    fn GetActiveUniformBlockName(
4728        &self,
4729        program: &WebGLProgram,
4730        block_index: u32,
4731    ) -> Option<DOMString> {
4732        handle_potential_webgl_error!(
4733            self.base,
4734            self.base.validate_ownership(program),
4735            return None
4736        );
4737        let name = handle_potential_webgl_error!(
4738            self.base,
4739            program.get_active_uniform_block_name(block_index),
4740            return None
4741        );
4742        Some(DOMString::from(name))
4743    }
4744
4745    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.16>
4746    fn UniformBlockBinding(&self, program: &WebGLProgram, block_index: u32, block_binding: u32) {
4747        handle_potential_webgl_error!(self.base, self.base.validate_ownership(program), return);
4748
4749        if block_binding >= self.base.limits().max_uniform_buffer_bindings {
4750            return self.base.webgl_error(InvalidValue);
4751        }
4752
4753        handle_potential_webgl_error!(
4754            self.base,
4755            program.bind_uniform_block(block_index, block_binding)
4756        )
4757    }
4758
4759    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.11>
4760    fn ClearBufferfv(
4761        &self,
4762        buffer: u32,
4763        draw_buffer: i32,
4764        values: Float32ArrayOrUnrestrictedFloatSequence,
4765        src_offset: u32,
4766    ) {
4767        let array = match values {
4768            Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
4769            Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
4770        };
4771        self.clear_buffer::<f32>(
4772            buffer,
4773            draw_buffer,
4774            &[constants::COLOR, constants::DEPTH],
4775            src_offset,
4776            array,
4777            WebGLCommand::ClearBufferfv,
4778        )
4779    }
4780
4781    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.11>
4782    fn ClearBufferiv(
4783        &self,
4784        buffer: u32,
4785        draw_buffer: i32,
4786        values: Int32ArrayOrLongSequence,
4787        src_offset: u32,
4788    ) {
4789        let array = match values {
4790            Int32ArrayOrLongSequence::Int32Array(v) => v.to_vec(),
4791            Int32ArrayOrLongSequence::LongSequence(v) => v,
4792        };
4793        self.clear_buffer::<i32>(
4794            buffer,
4795            draw_buffer,
4796            &[constants::COLOR, constants::STENCIL],
4797            src_offset,
4798            array,
4799            WebGLCommand::ClearBufferiv,
4800        )
4801    }
4802
4803    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.11>
4804    fn ClearBufferuiv(
4805        &self,
4806        buffer: u32,
4807        draw_buffer: i32,
4808        values: Uint32ArrayOrUnsignedLongSequence,
4809        src_offset: u32,
4810    ) {
4811        let array = match values {
4812            Uint32ArrayOrUnsignedLongSequence::Uint32Array(v) => v.to_vec(),
4813            Uint32ArrayOrUnsignedLongSequence::UnsignedLongSequence(v) => v,
4814        };
4815        self.clear_buffer::<u32>(
4816            buffer,
4817            draw_buffer,
4818            &[constants::COLOR],
4819            src_offset,
4820            array,
4821            WebGLCommand::ClearBufferuiv,
4822        )
4823    }
4824
4825    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.11>
4826    fn ClearBufferfi(&self, buffer: u32, draw_buffer: i32, depth: f32, stencil: i32) {
4827        if buffer != constants::DEPTH_STENCIL {
4828            return self.base.webgl_error(InvalidEnum);
4829        }
4830
4831        handle_potential_webgl_error!(
4832            self.base,
4833            self.clearbuffer_array_size(buffer, draw_buffer),
4834            return
4835        );
4836
4837        self.base.send_command(WebGLCommand::ClearBufferfi(
4838            buffer,
4839            draw_buffer,
4840            depth,
4841            stencil,
4842        ));
4843    }
4844
4845    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.4>
4846    fn InvalidateFramebuffer(&self, target: u32, attachments: Vec<u32>) {
4847        if !self.valid_fb_attachment_values(target, &attachments) {
4848            return;
4849        }
4850
4851        self.base
4852            .send_command(WebGLCommand::InvalidateFramebuffer(target, attachments))
4853    }
4854
4855    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.4>
4856    fn InvalidateSubFramebuffer(
4857        &self,
4858        target: u32,
4859        attachments: Vec<u32>,
4860        x: i32,
4861        y: i32,
4862        width: i32,
4863        height: i32,
4864    ) {
4865        if !self.valid_fb_attachment_values(target, &attachments) {
4866            return;
4867        }
4868
4869        if width < 0 || height < 0 {
4870            return self.base.webgl_error(InvalidValue);
4871        }
4872
4873        self.base
4874            .send_command(WebGLCommand::InvalidateSubFramebuffer(
4875                target,
4876                attachments,
4877                x,
4878                y,
4879                width,
4880                height,
4881            ))
4882    }
4883
4884    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.4>
4885    fn FramebufferTextureLayer(
4886        &self,
4887        target: u32,
4888        attachment: u32,
4889        texture: Option<&WebGLTexture>,
4890        level: i32,
4891        layer: i32,
4892    ) {
4893        if let Some(tex) = texture {
4894            handle_potential_webgl_error!(self.base, self.base.validate_ownership(tex), return);
4895        }
4896
4897        let fb_slot = match target {
4898            constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
4899                self.base.get_draw_framebuffer_slot()
4900            },
4901            constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
4902            _ => return self.base.webgl_error(InvalidEnum),
4903        };
4904
4905        match fb_slot.get() {
4906            Some(fb) => handle_potential_webgl_error!(
4907                self.base,
4908                fb.texture_layer(attachment, texture, level, layer)
4909            ),
4910            None => self.base.webgl_error(InvalidOperation),
4911        }
4912    }
4913
4914    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.5>
4915    #[expect(unsafe_code)]
4916    fn GetInternalformatParameter(
4917        &self,
4918        cx: &mut JSContext,
4919        target: u32,
4920        internal_format: u32,
4921        pname: u32,
4922        mut retval: MutableHandleValue,
4923    ) {
4924        if target != constants::RENDERBUFFER {
4925            self.base.webgl_error(InvalidEnum);
4926            return retval.set(NullValue());
4927        }
4928
4929        match handle_potential_webgl_error!(
4930            self.base,
4931            InternalFormatParameter::from_u32(pname),
4932            return retval.set(NullValue())
4933        ) {
4934            InternalFormatParameter::IntVec(param) => unsafe {
4935                let (sender, receiver) = webgl_channel().unwrap();
4936                self.base
4937                    .send_command(WebGLCommand::GetInternalFormatIntVec(
4938                        target,
4939                        internal_format,
4940                        param,
4941                        sender,
4942                    ));
4943
4944                rooted!(&in(cx) let mut rval = ptr::null_mut::<JSObject>());
4945                Int32Array::create(
4946                    cx.raw_cx(),
4947                    CreateWith::Slice(&receiver.recv().unwrap()),
4948                    rval.handle_mut(),
4949                )
4950                .unwrap();
4951                retval.set(ObjectValue(rval.get()))
4952            },
4953        }
4954    }
4955
4956    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.5>
4957    fn RenderbufferStorageMultisample(
4958        &self,
4959        target: u32,
4960        samples: i32,
4961        internal_format: u32,
4962        width: i32,
4963        height: i32,
4964    ) {
4965        self.base
4966            .renderbuffer_storage(target, samples, internal_format, width, height)
4967    }
4968
4969    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.4>
4970    fn ReadBuffer(&self, src: u32) {
4971        match src {
4972            constants::BACK | constants::NONE => {},
4973            _ if self.base.valid_color_attachment_enum(src) => {},
4974            _ => return self.base.webgl_error(InvalidEnum),
4975        }
4976
4977        if let Some(fb) = self.base.get_read_framebuffer_slot().get() {
4978            handle_potential_webgl_error!(self.base, fb.set_read_buffer(src))
4979        } else {
4980            match src {
4981                constants::NONE | constants::BACK => {},
4982                _ => return self.base.webgl_error(InvalidOperation),
4983            }
4984
4985            self.default_fb_readbuffer.set(src);
4986            self.base.send_command(WebGLCommand::ReadBuffer(src));
4987        }
4988    }
4989
4990    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.11>
4991    fn DrawBuffers(&self, buffers: Vec<u32>) {
4992        if let Some(fb) = self.base.get_draw_framebuffer_slot().get() {
4993            handle_potential_webgl_error!(self.base, fb.set_draw_buffers(buffers))
4994        } else {
4995            if buffers.len() != 1 {
4996                return self.base.webgl_error(InvalidOperation);
4997            }
4998
4999            match buffers[0] {
5000                constants::NONE | constants::BACK => {},
5001                _ => return self.base.webgl_error(InvalidOperation),
5002            }
5003
5004            self.default_fb_drawbuffer.set(buffers[0]);
5005            self.base.send_command(WebGLCommand::DrawBuffers(buffers));
5006        }
5007    }
5008
5009    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.6>
5010    fn TexStorage2D(
5011        &self,
5012        target: u32,
5013        levels: i32,
5014        internal_format: u32,
5015        width: i32,
5016        height: i32,
5017    ) {
5018        self.tex_storage(2, target, levels, internal_format, width, height, 1)
5019    }
5020
5021    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.7.6>
5022    fn TexStorage3D(
5023        &self,
5024        target: u32,
5025        levels: i32,
5026        internal_format: u32,
5027        width: i32,
5028        height: i32,
5029        depth: i32,
5030    ) {
5031        self.tex_storage(3, target, levels, internal_format, width, height, depth)
5032    }
5033
5034    /// <https://immersive-web.github.io/webxr/#dom-webglrenderingcontextbase-makexrcompatible>
5035    #[cfg(feature = "webxr")]
5036    fn MakeXRCompatible(&self, cx: &mut js::context::JSContext) -> Rc<Promise> {
5037        // XXXManishearth Fill in with compatibility checks when rust-webxr supports this
5038        Promise::new_resolved(cx, &self.global(), ())
5039    }
5040}
5041
5042impl WebGL2RenderingContextHelpers for WebGL2RenderingContext {
5043    fn is_webgl2_enabled(cx: &mut js::context::JSContext, global: HandleObject) -> bool {
5044        Self::is_webgl2_enabled(cx, global)
5045    }
5046}