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