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