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 base::generic_channel::{self, GenericSharedMemory};
12use bitflags::bitflags;
13use canvas_traits::webgl::WebGLError::*;
14use canvas_traits::webgl::{
15    AlphaTreatment, GLContextAttributes, InternalFormatParameter, TexDataType, TexFormat,
16    WebGLCommand, WebGLContextId, WebGLResult, WebGLVersion, YAxisTreatment, webgl_channel,
17};
18use dom_struct::dom_struct;
19use euclid::default::{Point2D, Rect, Size2D};
20use js::jsapi::{JSObject, Type};
21use js::jsval::{BooleanValue, DoubleValue, Int32Value, NullValue, ObjectValue, UInt32Value};
22use js::rust::{CustomAutoRooterGuard, HandleObject, MutableHandleValue};
23use js::typedarray::{ArrayBufferView, CreateWith, Float32, Int32Array, Uint32, Uint32Array};
24use pixels::{Alpha, Snapshot};
25use script_bindings::conversions::SafeToJSValConvertible;
26use script_bindings::interfaces::WebGL2RenderingContextHelpers;
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]
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/#5.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::note());
702                },
703                Some(Texture(texture)) => {
704                    texture.safe_to_jsval(cx, rval, CanGc::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::note());
1046                return;
1047            },
1048            constants::SHADING_LANGUAGE_VERSION => {
1049                "WebGL GLSL ES 3.00".safe_to_jsval(cx, rval, CanGc::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::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::note());
1075                return;
1076            },
1077            constants::COPY_WRITE_BUFFER_BINDING => {
1078                self.bound_copy_write_buffer
1079                    .get()
1080                    .safe_to_jsval(cx, rval, CanGc::note());
1081                return;
1082            },
1083            constants::PIXEL_PACK_BUFFER_BINDING => {
1084                self.bound_pixel_pack_buffer
1085                    .get()
1086                    .safe_to_jsval(cx, rval, CanGc::note());
1087                return;
1088            },
1089            constants::PIXEL_UNPACK_BUFFER_BINDING => {
1090                self.bound_pixel_unpack_buffer
1091                    .get()
1092                    .safe_to_jsval(cx, rval, CanGc::note());
1093                return;
1094            },
1095            constants::TRANSFORM_FEEDBACK_BUFFER_BINDING => {
1096                self.bound_transform_feedback_buffer
1097                    .get()
1098                    .safe_to_jsval(cx, rval, CanGc::note());
1099                return;
1100            },
1101            constants::UNIFORM_BUFFER_BINDING => {
1102                self.bound_uniform_buffer
1103                    .get()
1104                    .safe_to_jsval(cx, rval, CanGc::note());
1105                return;
1106            },
1107            constants::TRANSFORM_FEEDBACK_BINDING => {
1108                self.current_transform_feedback
1109                    .get()
1110                    .safe_to_jsval(cx, rval, CanGc::note());
1111                return;
1112            },
1113            constants::ELEMENT_ARRAY_BUFFER_BINDING => {
1114                let buffer = self.current_vao().element_array_buffer().get();
1115                buffer.safe_to_jsval(cx, rval, CanGc::note());
1116                return;
1117            },
1118            constants::VERTEX_ARRAY_BINDING => {
1119                let vao = self.current_vao();
1120                let vao = vao.id().map(|_| &*vao);
1121                vao.safe_to_jsval(cx, rval, CanGc::note());
1122                return;
1123            },
1124            // NOTE: DRAW_FRAMEBUFFER_BINDING is the same as FRAMEBUFFER_BINDING, handled on the WebGL1 side
1125            constants::READ_FRAMEBUFFER_BINDING => {
1126                self.base
1127                    .get_read_framebuffer_slot()
1128                    .get()
1129                    .safe_to_jsval(cx, rval, CanGc::note());
1130                return;
1131            },
1132            constants::READ_BUFFER => {
1133                let buffer = match self.base.get_read_framebuffer_slot().get() {
1134                    Some(fb) => fb.read_buffer(),
1135                    None => self.default_fb_readbuffer.get(),
1136                };
1137                rval.set(UInt32Value(buffer));
1138                return;
1139            },
1140            constants::DRAW_BUFFER0..=constants::DRAW_BUFFER15 => {
1141                let buffer = match self.base.get_read_framebuffer_slot().get() {
1142                    Some(fb) => {
1143                        let idx = parameter - constants::DRAW_BUFFER0;
1144                        fb.draw_buffer_i(idx as usize)
1145                    },
1146                    None if parameter == constants::DRAW_BUFFER0 => {
1147                        self.default_fb_readbuffer.get()
1148                    },
1149                    None => constants::NONE,
1150                };
1151                rval.set(UInt32Value(buffer));
1152                return;
1153            },
1154            constants::MAX_TEXTURE_LOD_BIAS => {
1155                rval.set(DoubleValue(self.base.limits().max_texture_lod_bias as f64));
1156                return;
1157            },
1158            constants::MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS => {
1159                rval.set(DoubleValue(
1160                    self.base.limits().max_combined_fragment_uniform_components as f64,
1161                ));
1162                return;
1163            },
1164            constants::MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS => {
1165                rval.set(DoubleValue(
1166                    self.base.limits().max_combined_vertex_uniform_components as f64,
1167                ));
1168                return;
1169            },
1170            constants::MAX_ELEMENT_INDEX => {
1171                rval.set(DoubleValue(self.base.limits().max_element_index as f64));
1172                return;
1173            },
1174            constants::MAX_UNIFORM_BLOCK_SIZE => {
1175                rval.set(DoubleValue(
1176                    self.base.limits().max_uniform_block_size as f64,
1177                ));
1178                return;
1179            },
1180            constants::MIN_PROGRAM_TEXEL_OFFSET => {
1181                rval.set(Int32Value(self.base.limits().min_program_texel_offset));
1182                return;
1183            },
1184            _ => {},
1185        }
1186
1187        let limit = match parameter {
1188            constants::MAX_3D_TEXTURE_SIZE => Some(self.base.limits().max_3d_texture_size),
1189            constants::MAX_ARRAY_TEXTURE_LAYERS => {
1190                Some(self.base.limits().max_array_texture_layers)
1191            },
1192            constants::MAX_COLOR_ATTACHMENTS => Some(self.base.limits().max_color_attachments),
1193            constants::MAX_COMBINED_UNIFORM_BLOCKS => {
1194                Some(self.base.limits().max_combined_uniform_blocks)
1195            },
1196            constants::MAX_DRAW_BUFFERS => Some(self.base.limits().max_draw_buffers),
1197            constants::MAX_ELEMENTS_INDICES => Some(self.base.limits().max_elements_indices),
1198            constants::MAX_ELEMENTS_VERTICES => Some(self.base.limits().max_elements_vertices),
1199            constants::MAX_FRAGMENT_INPUT_COMPONENTS => {
1200                Some(self.base.limits().max_fragment_input_components)
1201            },
1202            constants::MAX_FRAGMENT_UNIFORM_BLOCKS => {
1203                Some(self.base.limits().max_fragment_uniform_blocks)
1204            },
1205            constants::MAX_FRAGMENT_UNIFORM_COMPONENTS => {
1206                Some(self.base.limits().max_fragment_uniform_components)
1207            },
1208            constants::MAX_PROGRAM_TEXEL_OFFSET => {
1209                Some(self.base.limits().max_program_texel_offset)
1210            },
1211            constants::MAX_SAMPLES => Some(self.base.limits().max_samples),
1212            constants::MAX_UNIFORM_BUFFER_BINDINGS => {
1213                Some(self.base.limits().max_uniform_buffer_bindings)
1214            },
1215            constants::MAX_VARYING_COMPONENTS => Some(self.base.limits().max_varying_components),
1216            constants::MAX_VERTEX_OUTPUT_COMPONENTS => {
1217                Some(self.base.limits().max_vertex_output_components)
1218            },
1219            constants::MAX_VERTEX_UNIFORM_BLOCKS => {
1220                Some(self.base.limits().max_vertex_uniform_blocks)
1221            },
1222            constants::MAX_VERTEX_UNIFORM_COMPONENTS => {
1223                Some(self.base.limits().max_vertex_uniform_components)
1224            },
1225            constants::UNIFORM_BUFFER_OFFSET_ALIGNMENT => {
1226                Some(self.base.limits().uniform_buffer_offset_alignment)
1227            },
1228            _ => None,
1229        };
1230        if let Some(limit) = limit {
1231            rval.set(UInt32Value(limit));
1232            return;
1233        }
1234
1235        self.base.GetParameter(cx, parameter, rval)
1236    }
1237
1238    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1239    fn GetTexParameter(&self, cx: JSContext, target: u32, pname: u32, retval: MutableHandleValue) {
1240        self.base.GetTexParameter(cx, target, pname, retval)
1241    }
1242
1243    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1244    fn GetError(&self) -> u32 {
1245        self.base.GetError()
1246    }
1247
1248    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.2>
1249    fn GetContextAttributes(&self) -> Option<WebGLContextAttributes> {
1250        self.base.GetContextAttributes()
1251    }
1252
1253    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.13>
1254    fn IsContextLost(&self) -> bool {
1255        self.base.IsContextLost()
1256    }
1257
1258    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.14>
1259    fn GetSupportedExtensions(&self) -> Option<Vec<DOMString>> {
1260        self.base.GetSupportedExtensions()
1261    }
1262
1263    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.14>
1264    fn GetExtension(&self, cx: JSContext, name: DOMString) -> Option<NonNull<JSObject>> {
1265        self.base.GetExtension(cx, name)
1266    }
1267
1268    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4>
1269    fn GetFramebufferAttachmentParameter(
1270        &self,
1271        cx: JSContext,
1272        target: u32,
1273        attachment: u32,
1274        pname: u32,
1275        mut rval: MutableHandleValue,
1276    ) {
1277        let fb_slot = match target {
1278            constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
1279                self.base.get_draw_framebuffer_slot()
1280            },
1281            constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
1282            _ => {
1283                self.base.webgl_error(InvalidEnum);
1284                rval.set(NullValue());
1285                return;
1286            },
1287        };
1288
1289        if let Some(fb) = fb_slot.get() {
1290            // A selected framebuffer is bound to the target
1291            handle_potential_webgl_error!(
1292                self.base,
1293                fb.validate_transparent(),
1294                return rval.set(NullValue())
1295            );
1296            handle_potential_webgl_error!(
1297                self.base,
1298                self.get_specific_fb_attachment_param(
1299                    cx,
1300                    &fb,
1301                    target,
1302                    attachment,
1303                    pname,
1304                    rval.reborrow()
1305                ),
1306                rval.set(NullValue())
1307            )
1308        } else {
1309            // The default framebuffer is bound to the target
1310            handle_potential_webgl_error!(
1311                self.base,
1312                self.get_default_fb_attachment_param(attachment, pname, rval.reborrow()),
1313                rval.set(NullValue())
1314            )
1315        }
1316    }
1317
1318    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
1319    fn GetRenderbufferParameter(
1320        &self,
1321        cx: JSContext,
1322        target: u32,
1323        pname: u32,
1324        retval: MutableHandleValue,
1325    ) {
1326        self.base
1327            .GetRenderbufferParameter(cx, target, pname, retval)
1328    }
1329
1330    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1331    fn ActiveTexture(&self, texture: u32) {
1332        self.base.ActiveTexture(texture)
1333    }
1334
1335    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1336    fn BlendColor(&self, r: f32, g: f32, b: f32, a: f32) {
1337        self.base.BlendColor(r, g, b, a)
1338    }
1339
1340    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1341    fn BlendEquation(&self, mode: u32) {
1342        self.base.BlendEquation(mode)
1343    }
1344
1345    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1346    fn BlendEquationSeparate(&self, mode_rgb: u32, mode_alpha: u32) {
1347        self.base.BlendEquationSeparate(mode_rgb, mode_alpha)
1348    }
1349
1350    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1351    fn BlendFunc(&self, src_factor: u32, dest_factor: u32) {
1352        self.base.BlendFunc(src_factor, dest_factor)
1353    }
1354
1355    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1356    fn BlendFuncSeparate(&self, src_rgb: u32, dest_rgb: u32, src_alpha: u32, dest_alpha: u32) {
1357        self.base
1358            .BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha)
1359    }
1360
1361    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1362    fn AttachShader(&self, program: &WebGLProgram, shader: &WebGLShader) {
1363        self.base.AttachShader(program, shader)
1364    }
1365
1366    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1367    fn DetachShader(&self, program: &WebGLProgram, shader: &WebGLShader) {
1368        self.base.DetachShader(program, shader)
1369    }
1370
1371    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1372    fn BindAttribLocation(&self, program: &WebGLProgram, index: u32, name: DOMString) {
1373        self.base.BindAttribLocation(program, index, name)
1374    }
1375
1376    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.2>
1377    fn BindBuffer(&self, target: u32, buffer: Option<&WebGLBuffer>) {
1378        let current_vao;
1379        let slot = match target {
1380            constants::COPY_READ_BUFFER => &self.bound_copy_read_buffer,
1381            constants::COPY_WRITE_BUFFER => &self.bound_copy_write_buffer,
1382            constants::PIXEL_PACK_BUFFER => &self.bound_pixel_pack_buffer,
1383            constants::PIXEL_UNPACK_BUFFER => &self.bound_pixel_unpack_buffer,
1384            constants::TRANSFORM_FEEDBACK_BUFFER => &self.bound_transform_feedback_buffer,
1385            constants::UNIFORM_BUFFER => &self.bound_uniform_buffer,
1386            constants::ELEMENT_ARRAY_BUFFER => {
1387                current_vao = self.current_vao();
1388                current_vao.element_array_buffer()
1389            },
1390            _ => return self.base.BindBuffer(target, buffer),
1391        };
1392        self.base.bind_buffer_maybe(slot, target, buffer);
1393    }
1394
1395    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
1396    fn BindFramebuffer(&self, target: u32, framebuffer: Option<&WebGLFramebuffer>) {
1397        handle_potential_webgl_error!(
1398            self.base,
1399            self.base.validate_new_framebuffer_binding(framebuffer),
1400            return
1401        );
1402
1403        let (bind_read, bind_draw) = match target {
1404            constants::FRAMEBUFFER => (true, true),
1405            constants::READ_FRAMEBUFFER => (true, false),
1406            constants::DRAW_FRAMEBUFFER => (false, true),
1407            _ => return self.base.webgl_error(InvalidEnum),
1408        };
1409        if bind_read {
1410            self.base.bind_framebuffer_to(
1411                target,
1412                framebuffer,
1413                self.base.get_read_framebuffer_slot(),
1414            );
1415        }
1416        if bind_draw {
1417            self.base.bind_framebuffer_to(
1418                target,
1419                framebuffer,
1420                self.base.get_draw_framebuffer_slot(),
1421            );
1422        }
1423    }
1424
1425    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
1426    fn BindRenderbuffer(&self, target: u32, renderbuffer: Option<&WebGLRenderbuffer>) {
1427        self.base.BindRenderbuffer(target, renderbuffer)
1428    }
1429
1430    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1431    fn BindTexture(&self, target: u32, texture: Option<&WebGLTexture>) {
1432        self.base.BindTexture(target, texture)
1433    }
1434
1435    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1436    fn GenerateMipmap(&self, target: u32) {
1437        self.base.GenerateMipmap(target)
1438    }
1439
1440    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
1441    fn BufferData_(&self, target: u32, data: Option<ArrayBufferViewOrArrayBuffer>, usage: u32) {
1442        let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return);
1443        let bound_buffer =
1444            handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
1445        self.base.buffer_data(target, data, usage, bound_buffer)
1446    }
1447
1448    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
1449    fn BufferData(&self, target: u32, size: i64, usage: u32) {
1450        let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return);
1451        let bound_buffer =
1452            handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
1453        self.base.buffer_data_(target, size, usage, bound_buffer)
1454    }
1455
1456    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.3>
1457    #[expect(unsafe_code)]
1458    fn BufferData__(
1459        &self,
1460        target: u32,
1461        data: CustomAutoRooterGuard<ArrayBufferView>,
1462        usage: u32,
1463        elem_offset: u32,
1464        length: u32,
1465    ) {
1466        let usage = handle_potential_webgl_error!(self.base, self.buffer_usage(usage), return);
1467        let bound_buffer =
1468            handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
1469        let bound_buffer =
1470            handle_potential_webgl_error!(self.base, bound_buffer.ok_or(InvalidOperation), return);
1471
1472        let elem_size = data.get_array_type().byte_size().unwrap();
1473        let elem_count = data.len() / elem_size;
1474        let elem_offset = elem_offset as usize;
1475        let byte_offset = elem_offset * elem_size;
1476
1477        if byte_offset > data.len() {
1478            return self.base.webgl_error(InvalidValue);
1479        }
1480
1481        let copy_count = if length == 0 {
1482            elem_count - elem_offset
1483        } else {
1484            length as usize
1485        };
1486        if copy_count == 0 {
1487            return;
1488        }
1489        let copy_bytes = copy_count * elem_size;
1490
1491        if byte_offset + copy_bytes > data.len() {
1492            return self.base.webgl_error(InvalidValue);
1493        }
1494
1495        let data_end = byte_offset + copy_bytes;
1496        let data: &[u8] = unsafe { &data.as_slice()[byte_offset..data_end] };
1497        handle_potential_webgl_error!(self.base, bound_buffer.buffer_data(target, data, usage));
1498    }
1499
1500    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
1501    fn BufferSubData(&self, target: u32, offset: i64, data: ArrayBufferViewOrArrayBuffer) {
1502        let bound_buffer =
1503            handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
1504        self.base
1505            .buffer_sub_data(target, offset, data, bound_buffer)
1506    }
1507
1508    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.3>
1509    #[expect(unsafe_code)]
1510    fn BufferSubData_(
1511        &self,
1512        target: u32,
1513        dst_byte_offset: i64,
1514        src_data: CustomAutoRooterGuard<ArrayBufferView>,
1515        src_elem_offset: u32,
1516        length: u32,
1517    ) {
1518        let bound_buffer =
1519            handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
1520        let bound_buffer =
1521            handle_potential_webgl_error!(self.base, bound_buffer.ok_or(InvalidOperation), return);
1522
1523        let src_elem_size = src_data.get_array_type().byte_size().unwrap();
1524        let src_elem_count = src_data.len() / src_elem_size;
1525        let src_elem_offset = src_elem_offset as usize;
1526        let src_byte_offset = src_elem_offset * src_elem_size;
1527
1528        if dst_byte_offset < 0 || src_byte_offset > src_data.len() {
1529            return self.base.webgl_error(InvalidValue);
1530        }
1531
1532        let copy_count = if length == 0 {
1533            src_elem_count - src_elem_offset
1534        } else {
1535            length as usize
1536        };
1537        if copy_count == 0 {
1538            return;
1539        }
1540        let copy_bytes = copy_count * src_elem_size;
1541
1542        let dst_byte_offset = dst_byte_offset as usize;
1543        if dst_byte_offset + copy_bytes > bound_buffer.capacity() ||
1544            src_byte_offset + copy_bytes > src_data.len()
1545        {
1546            return self.base.webgl_error(InvalidValue);
1547        }
1548
1549        let (sender, receiver) = generic_channel::channel().unwrap();
1550        self.base.send_command(WebGLCommand::BufferSubData(
1551            target,
1552            dst_byte_offset as isize,
1553            receiver,
1554        ));
1555        let src_end = src_byte_offset + copy_bytes;
1556        let data: &[u8] = unsafe { &src_data.as_slice()[src_byte_offset..src_end] };
1557        let buffer = GenericSharedMemory::from_bytes(data);
1558        sender.send(buffer).unwrap();
1559    }
1560
1561    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.3>
1562    fn CopyBufferSubData(
1563        &self,
1564        read_target: u32,
1565        write_target: u32,
1566        read_offset: i64,
1567        write_offset: i64,
1568        size: i64,
1569    ) {
1570        if read_offset < 0 || write_offset < 0 || size < 0 {
1571            return self.base.webgl_error(InvalidValue);
1572        }
1573
1574        let read_buffer =
1575            handle_potential_webgl_error!(self.base, self.bound_buffer(read_target), return);
1576        let read_buffer =
1577            handle_potential_webgl_error!(self.base, read_buffer.ok_or(InvalidOperation), return);
1578
1579        let write_buffer =
1580            handle_potential_webgl_error!(self.base, self.bound_buffer(write_target), return);
1581        let write_buffer =
1582            handle_potential_webgl_error!(self.base, write_buffer.ok_or(InvalidOperation), return);
1583
1584        let read_until = read_offset + size;
1585        let write_until = write_offset + size;
1586        if read_until as usize > read_buffer.capacity() ||
1587            write_until as usize > write_buffer.capacity()
1588        {
1589            return self.base.webgl_error(InvalidValue);
1590        }
1591
1592        if read_target == write_target {
1593            let is_separate = read_until <= write_offset || write_until <= read_offset;
1594            if !is_separate {
1595                return self.base.webgl_error(InvalidValue);
1596            }
1597        }
1598        let src_is_elemarray = read_buffer
1599            .target()
1600            .is_some_and(|t| t == constants::ELEMENT_ARRAY_BUFFER);
1601        let dst_is_elemarray = write_buffer
1602            .target()
1603            .is_some_and(|t| t == constants::ELEMENT_ARRAY_BUFFER);
1604        if src_is_elemarray != dst_is_elemarray {
1605            return self.base.webgl_error(InvalidOperation);
1606        }
1607
1608        self.base.send_command(WebGLCommand::CopyBufferSubData(
1609            read_target,
1610            write_target,
1611            read_offset,
1612            write_offset,
1613            size,
1614        ));
1615    }
1616
1617    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.3>
1618    #[expect(unsafe_code)]
1619    fn GetBufferSubData(
1620        &self,
1621        target: u32,
1622        src_byte_offset: i64,
1623        mut dst_buffer: CustomAutoRooterGuard<ArrayBufferView>,
1624        dst_elem_offset: u32,
1625        length: u32,
1626    ) {
1627        let bound_buffer =
1628            handle_potential_webgl_error!(self.base, self.bound_buffer(target), return);
1629        let bound_buffer =
1630            handle_potential_webgl_error!(self.base, bound_buffer.ok_or(InvalidOperation), return);
1631
1632        let dst_elem_size = dst_buffer.get_array_type().byte_size().unwrap();
1633        let dst_elem_count = dst_buffer.len() / dst_elem_size;
1634        let dst_elem_offset = dst_elem_offset as usize;
1635        let dst_byte_offset = dst_elem_offset * dst_elem_size;
1636
1637        if src_byte_offset < 0 || dst_byte_offset > dst_buffer.len() {
1638            return self.base.webgl_error(InvalidValue);
1639        }
1640
1641        let copy_count = if length == 0 {
1642            dst_elem_count - dst_elem_offset
1643        } else {
1644            length as usize
1645        };
1646        if copy_count == 0 {
1647            return;
1648        }
1649        let copy_bytes = copy_count * dst_elem_size;
1650
1651        // TODO(mmatyas): Transform Feedback
1652
1653        let src_byte_offset = src_byte_offset as usize;
1654        if src_byte_offset + copy_bytes > bound_buffer.capacity() ||
1655            dst_byte_offset + copy_bytes > dst_buffer.len()
1656        {
1657            return self.base.webgl_error(InvalidValue);
1658        }
1659
1660        let (sender, receiver) = generic_channel::channel().unwrap();
1661        self.base.send_command(WebGLCommand::GetBufferSubData(
1662            target,
1663            src_byte_offset,
1664            copy_bytes,
1665            sender,
1666        ));
1667        let data = receiver.recv().unwrap();
1668        let dst_end = dst_byte_offset + copy_bytes;
1669        unsafe {
1670            dst_buffer.as_mut_slice()[dst_byte_offset..dst_end].copy_from_slice(&data);
1671        }
1672    }
1673
1674    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.6>
1675    #[expect(unsafe_code)]
1676    fn CompressedTexImage2D(
1677        &self,
1678        target: u32,
1679        level: i32,
1680        internal_format: u32,
1681        width: i32,
1682        height: i32,
1683        border: i32,
1684        pixels: CustomAutoRooterGuard<ArrayBufferView>,
1685        src_offset: u32,
1686        src_length_override: u32,
1687    ) {
1688        let mut data = unsafe { pixels.as_slice() };
1689        let start = src_offset as usize;
1690        let end = (src_offset + src_length_override) as usize;
1691        if start > data.len() || end > data.len() {
1692            self.base.webgl_error(InvalidValue);
1693            return;
1694        }
1695        if src_length_override != 0 {
1696            data = &data[start..end];
1697        }
1698        self.base.compressed_tex_image_2d(
1699            target,
1700            level,
1701            internal_format,
1702            width,
1703            height,
1704            border,
1705            data,
1706        )
1707    }
1708
1709    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1710    #[expect(unsafe_code)]
1711    fn CompressedTexSubImage2D(
1712        &self,
1713        target: u32,
1714        level: i32,
1715        xoffset: i32,
1716        yoffset: i32,
1717        width: i32,
1718        height: i32,
1719        format: u32,
1720        pixels: CustomAutoRooterGuard<ArrayBufferView>,
1721        src_offset: u32,
1722        src_length_override: u32,
1723    ) {
1724        let mut data = unsafe { pixels.as_slice() };
1725        let start = src_offset as usize;
1726        let end = (src_offset + src_length_override) as usize;
1727        if start > data.len() || end > data.len() {
1728            self.base.webgl_error(InvalidValue);
1729            return;
1730        }
1731        if src_length_override != 0 {
1732            data = &data[start..end];
1733        }
1734        self.base.compressed_tex_sub_image_2d(
1735            target, level, xoffset, yoffset, width, height, format, data,
1736        )
1737    }
1738
1739    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1740    fn CopyTexImage2D(
1741        &self,
1742        target: u32,
1743        level: i32,
1744        internal_format: u32,
1745        x: i32,
1746        y: i32,
1747        width: i32,
1748        height: i32,
1749        border: i32,
1750    ) {
1751        self.base
1752            .CopyTexImage2D(target, level, internal_format, x, y, width, height, border)
1753    }
1754
1755    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1756    fn CopyTexSubImage2D(
1757        &self,
1758        target: u32,
1759        level: i32,
1760        xoffset: i32,
1761        yoffset: i32,
1762        x: i32,
1763        y: i32,
1764        width: i32,
1765        height: i32,
1766    ) {
1767        self.base
1768            .CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height)
1769    }
1770
1771    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11>
1772    fn Clear(&self, mask: u32) {
1773        self.base.Clear(mask)
1774    }
1775
1776    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1777    fn ClearColor(&self, red: f32, green: f32, blue: f32, alpha: f32) {
1778        self.base.ClearColor(red, green, blue, alpha)
1779    }
1780
1781    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1782    fn ClearDepth(&self, depth: f32) {
1783        self.base.ClearDepth(depth)
1784    }
1785
1786    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1787    fn ClearStencil(&self, stencil: i32) {
1788        self.base.ClearStencil(stencil)
1789    }
1790
1791    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1792    fn ColorMask(&self, r: bool, g: bool, b: bool, a: bool) {
1793        self.base.ColorMask(r, g, b, a)
1794    }
1795
1796    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1797    fn CullFace(&self, mode: u32) {
1798        self.base.CullFace(mode)
1799    }
1800
1801    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1802    fn FrontFace(&self, mode: u32) {
1803        self.base.FrontFace(mode)
1804    }
1805    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1806    fn DepthFunc(&self, func: u32) {
1807        self.base.DepthFunc(func)
1808    }
1809
1810    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1811    fn DepthMask(&self, flag: bool) {
1812        self.base.DepthMask(flag)
1813    }
1814
1815    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1816    fn DepthRange(&self, near: f32, far: f32) {
1817        self.base.DepthRange(near, far)
1818    }
1819
1820    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1821    fn Enable(&self, cap: u32) {
1822        match cap {
1823            constants::RASTERIZER_DISCARD => {
1824                self.enable_rasterizer_discard.set(true);
1825                self.base.send_command(WebGLCommand::Enable(cap));
1826            },
1827            _ => self.base.Enable(cap),
1828        }
1829    }
1830
1831    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
1832    fn Disable(&self, cap: u32) {
1833        match cap {
1834            constants::RASTERIZER_DISCARD => {
1835                self.enable_rasterizer_discard.set(false);
1836                self.base.send_command(WebGLCommand::Disable(cap));
1837            },
1838            _ => self.base.Disable(cap),
1839        }
1840    }
1841
1842    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1843    fn CompileShader(&self, shader: &WebGLShader) {
1844        self.base.CompileShader(shader)
1845    }
1846
1847    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
1848    fn CreateBuffer(&self) -> Option<DomRoot<WebGLBuffer>> {
1849        self.base.CreateBuffer()
1850    }
1851
1852    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
1853    fn CreateFramebuffer(&self) -> Option<DomRoot<WebGLFramebuffer>> {
1854        self.base.CreateFramebuffer()
1855    }
1856
1857    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
1858    fn CreateRenderbuffer(&self) -> Option<DomRoot<WebGLRenderbuffer>> {
1859        self.base.CreateRenderbuffer()
1860    }
1861
1862    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1863    fn CreateTexture(&self) -> Option<DomRoot<WebGLTexture>> {
1864        self.base.CreateTexture()
1865    }
1866
1867    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1868    fn CreateProgram(&self) -> Option<DomRoot<WebGLProgram>> {
1869        self.base.CreateProgram()
1870    }
1871
1872    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1873    fn CreateShader(&self, shader_type: u32) -> Option<DomRoot<WebGLShader>> {
1874        self.base.CreateShader(shader_type)
1875    }
1876
1877    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17>
1878    fn CreateVertexArray(&self) -> Option<DomRoot<WebGLVertexArrayObject>> {
1879        self.base.create_vertex_array_webgl2()
1880    }
1881
1882    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
1883    fn DeleteBuffer(&self, buffer: Option<&WebGLBuffer>) {
1884        let buffer = match buffer {
1885            Some(buffer) => buffer,
1886            None => return,
1887        };
1888        handle_potential_webgl_error!(self.base, self.base.validate_ownership(buffer), return);
1889        if buffer.is_marked_for_deletion() {
1890            return;
1891        }
1892        self.current_vao().unbind_buffer(buffer);
1893        self.unbind_from(self.base.array_buffer_slot(), buffer);
1894        self.unbind_from(&self.bound_copy_read_buffer, buffer);
1895        self.unbind_from(&self.bound_copy_write_buffer, buffer);
1896        self.unbind_from(&self.bound_pixel_pack_buffer, buffer);
1897        self.unbind_from(&self.bound_pixel_unpack_buffer, buffer);
1898        self.unbind_from(&self.bound_transform_feedback_buffer, buffer);
1899        self.unbind_from(&self.bound_uniform_buffer, buffer);
1900
1901        for binding in self.indexed_uniform_buffer_bindings.iter() {
1902            self.unbind_from(&binding.buffer, buffer);
1903        }
1904        for binding in self.indexed_transform_feedback_buffer_bindings.iter() {
1905            self.unbind_from(&binding.buffer, buffer);
1906        }
1907
1908        buffer.mark_for_deletion(Operation::Infallible);
1909    }
1910
1911    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
1912    fn DeleteFramebuffer(&self, framebuffer: Option<&WebGLFramebuffer>) {
1913        self.base.DeleteFramebuffer(framebuffer)
1914    }
1915
1916    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
1917    fn DeleteRenderbuffer(&self, renderbuffer: Option<&WebGLRenderbuffer>) {
1918        self.base.DeleteRenderbuffer(renderbuffer)
1919    }
1920
1921    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
1922    fn DeleteTexture(&self, texture: Option<&WebGLTexture>) {
1923        self.base.DeleteTexture(texture)
1924    }
1925
1926    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1927    fn DeleteProgram(&self, program: Option<&WebGLProgram>) {
1928        self.base.DeleteProgram(program)
1929    }
1930
1931    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1932    fn DeleteShader(&self, shader: Option<&WebGLShader>) {
1933        self.base.DeleteShader(shader)
1934    }
1935
1936    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17>
1937    fn DeleteVertexArray(&self, vertex_array: Option<&WebGLVertexArrayObject>) {
1938        self.base.delete_vertex_array_webgl2(vertex_array);
1939    }
1940
1941    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11>
1942    fn DrawArrays(&self, mode: u32, first: i32, count: i32) {
1943        self.validate_uniform_block_for_draw();
1944        self.validate_vertex_attribs_for_draw();
1945        self.base.DrawArrays(mode, first, count)
1946    }
1947
1948    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11>
1949    fn DrawElements(&self, mode: u32, count: i32, type_: u32, offset: i64) {
1950        self.validate_uniform_block_for_draw();
1951        self.validate_vertex_attribs_for_draw();
1952        self.base.DrawElements(mode, count, type_, offset)
1953    }
1954
1955    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
1956    fn EnableVertexAttribArray(&self, attrib_id: u32) {
1957        self.base.EnableVertexAttribArray(attrib_id)
1958    }
1959
1960    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
1961    fn DisableVertexAttribArray(&self, attrib_id: u32) {
1962        self.base.DisableVertexAttribArray(attrib_id)
1963    }
1964
1965    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
1966    fn GetActiveUniform(
1967        &self,
1968        program: &WebGLProgram,
1969        index: u32,
1970    ) -> Option<DomRoot<WebGLActiveInfo>> {
1971        self.base.GetActiveUniform(program, index)
1972    }
1973
1974    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
1975    fn GetActiveAttrib(
1976        &self,
1977        program: &WebGLProgram,
1978        index: u32,
1979    ) -> Option<DomRoot<WebGLActiveInfo>> {
1980        self.base.GetActiveAttrib(program, index)
1981    }
1982
1983    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
1984    fn GetAttribLocation(&self, program: &WebGLProgram, name: DOMString) -> i32 {
1985        self.base.GetAttribLocation(program, name)
1986    }
1987
1988    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.7>
1989    fn GetFragDataLocation(&self, program: &WebGLProgram, name: DOMString) -> i32 {
1990        handle_potential_webgl_error!(self.base, self.base.validate_ownership(program), return -1);
1991        handle_potential_webgl_error!(self.base, program.get_frag_data_location(name), -1)
1992    }
1993
1994    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
1995    fn GetProgramInfoLog(&self, program: &WebGLProgram) -> Option<DOMString> {
1996        self.base.GetProgramInfoLog(program)
1997    }
1998
1999    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2000    fn GetProgramParameter(
2001        &self,
2002        cx: JSContext,
2003        program: &WebGLProgram,
2004        param_id: u32,
2005        mut retval: MutableHandleValue,
2006    ) {
2007        handle_potential_webgl_error!(
2008            self.base,
2009            self.base.validate_ownership(program),
2010            return retval.set(NullValue())
2011        );
2012        if program.is_deleted() {
2013            self.base.webgl_error(InvalidOperation);
2014            return retval.set(NullValue());
2015        }
2016        match param_id {
2017            constants::TRANSFORM_FEEDBACK_VARYINGS => {
2018                retval.set(Int32Value(program.transform_feedback_varyings_length()))
2019            },
2020            constants::TRANSFORM_FEEDBACK_BUFFER_MODE => {
2021                retval.set(Int32Value(program.transform_feedback_buffer_mode()))
2022            },
2023            _ => self.base.GetProgramParameter(cx, program, param_id, retval),
2024        }
2025    }
2026
2027    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2028    fn GetShaderInfoLog(&self, shader: &WebGLShader) -> Option<DOMString> {
2029        self.base.GetShaderInfoLog(shader)
2030    }
2031
2032    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2033    fn GetShaderParameter(
2034        &self,
2035        cx: JSContext,
2036        shader: &WebGLShader,
2037        param_id: u32,
2038        retval: MutableHandleValue,
2039    ) {
2040        self.base.GetShaderParameter(cx, shader, param_id, retval)
2041    }
2042
2043    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2044    fn GetShaderPrecisionFormat(
2045        &self,
2046        shader_type: u32,
2047        precision_type: u32,
2048    ) -> Option<DomRoot<WebGLShaderPrecisionFormat>> {
2049        self.base
2050            .GetShaderPrecisionFormat(shader_type, precision_type)
2051    }
2052
2053    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.2>
2054    fn GetIndexedParameter(
2055        &self,
2056        cx: JSContext,
2057        target: u32,
2058        index: u32,
2059        mut retval: MutableHandleValue,
2060    ) {
2061        let bindings = match target {
2062            constants::TRANSFORM_FEEDBACK_BUFFER_BINDING |
2063            constants::TRANSFORM_FEEDBACK_BUFFER_SIZE |
2064            constants::TRANSFORM_FEEDBACK_BUFFER_START => {
2065                &self.indexed_transform_feedback_buffer_bindings
2066            },
2067            constants::UNIFORM_BUFFER_BINDING |
2068            constants::UNIFORM_BUFFER_SIZE |
2069            constants::UNIFORM_BUFFER_START => &self.indexed_uniform_buffer_bindings,
2070            _ => {
2071                self.base.webgl_error(InvalidEnum);
2072                return retval.set(NullValue());
2073            },
2074        };
2075
2076        let binding = match bindings.get(index as usize) {
2077            Some(binding) => binding,
2078            None => {
2079                self.base.webgl_error(InvalidValue);
2080                return retval.set(NullValue());
2081            },
2082        };
2083
2084        match target {
2085            constants::TRANSFORM_FEEDBACK_BUFFER_BINDING | constants::UNIFORM_BUFFER_BINDING => {
2086                binding
2087                    .buffer
2088                    .get()
2089                    .safe_to_jsval(cx, retval, CanGc::note())
2090            },
2091            constants::TRANSFORM_FEEDBACK_BUFFER_START | constants::UNIFORM_BUFFER_START => {
2092                retval.set(Int32Value(binding.start.get() as _))
2093            },
2094            constants::TRANSFORM_FEEDBACK_BUFFER_SIZE | constants::UNIFORM_BUFFER_SIZE => {
2095                retval.set(Int32Value(binding.size.get() as _))
2096            },
2097            _ => unreachable!(),
2098        }
2099    }
2100
2101    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2102    fn GetUniformLocation(
2103        &self,
2104        program: &WebGLProgram,
2105        name: DOMString,
2106    ) -> Option<DomRoot<WebGLUniformLocation>> {
2107        self.base.GetUniformLocation(program, name)
2108    }
2109
2110    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2111    fn GetVertexAttrib(&self, cx: JSContext, index: u32, pname: u32, retval: MutableHandleValue) {
2112        self.base.GetVertexAttrib(cx, index, pname, retval)
2113    }
2114
2115    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2116    fn GetVertexAttribOffset(&self, index: u32, pname: u32) -> i64 {
2117        self.base.GetVertexAttribOffset(index, pname)
2118    }
2119
2120    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2121    fn Hint(&self, target: u32, mode: u32) {
2122        self.base.Hint(target, mode)
2123    }
2124
2125    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
2126    fn IsBuffer(&self, buffer: Option<&WebGLBuffer>) -> bool {
2127        self.base.IsBuffer(buffer)
2128    }
2129
2130    // TODO: We could write this without IPC, recording the calls to `enable` and `disable`.
2131    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.2>
2132    fn IsEnabled(&self, cap: u32) -> bool {
2133        match cap {
2134            constants::RASTERIZER_DISCARD => self.enable_rasterizer_discard.get(),
2135            _ => self.base.IsEnabled(cap),
2136        }
2137    }
2138
2139    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
2140    fn IsFramebuffer(&self, frame_buffer: Option<&WebGLFramebuffer>) -> bool {
2141        self.base.IsFramebuffer(frame_buffer)
2142    }
2143
2144    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2145    fn IsProgram(&self, program: Option<&WebGLProgram>) -> bool {
2146        self.base.IsProgram(program)
2147    }
2148
2149    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
2150    fn IsRenderbuffer(&self, render_buffer: Option<&WebGLRenderbuffer>) -> bool {
2151        self.base.IsRenderbuffer(render_buffer)
2152    }
2153
2154    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2155    fn IsShader(&self, shader: Option<&WebGLShader>) -> bool {
2156        self.base.IsShader(shader)
2157    }
2158
2159    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
2160    fn IsTexture(&self, texture: Option<&WebGLTexture>) -> bool {
2161        self.base.IsTexture(texture)
2162    }
2163
2164    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17>
2165    fn IsVertexArray(&self, vertex_array: Option<&WebGLVertexArrayObject>) -> bool {
2166        self.base.is_vertex_array_webgl2(vertex_array)
2167    }
2168
2169    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2170    fn LineWidth(&self, width: f32) {
2171        self.base.LineWidth(width)
2172    }
2173
2174    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.2>
2175    fn PixelStorei(&self, param_name: u32, param_value: i32) {
2176        if param_value < 0 {
2177            return self.base.webgl_error(InvalidValue);
2178        }
2179
2180        match param_name {
2181            constants::PACK_ROW_LENGTH => self.texture_pack_row_length.set(param_value as _),
2182            constants::PACK_SKIP_PIXELS => self.texture_pack_skip_pixels.set(param_value as _),
2183            constants::PACK_SKIP_ROWS => self.texture_pack_skip_rows.set(param_value as _),
2184            _ => self.base.PixelStorei(param_name, param_value),
2185        }
2186    }
2187
2188    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2189    fn PolygonOffset(&self, factor: f32, units: f32) {
2190        self.base.PolygonOffset(factor, units)
2191    }
2192
2193    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.12>
2194    fn ReadPixels(
2195        &self,
2196        x: i32,
2197        y: i32,
2198        width: i32,
2199        height: i32,
2200        format: u32,
2201        pixel_type: u32,
2202        mut pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
2203    ) {
2204        let pixels =
2205            handle_potential_webgl_error!(self.base, pixels.as_mut().ok_or(InvalidValue), return);
2206
2207        self.read_pixels_into(x, y, width, height, format, pixel_type, pixels, 0)
2208    }
2209
2210    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.10>
2211    fn ReadPixels_(
2212        &self,
2213        x: i32,
2214        y: i32,
2215        width: i32,
2216        height: i32,
2217        format: u32,
2218        pixel_type: u32,
2219        dst_byte_offset: i64,
2220    ) {
2221        handle_potential_webgl_error!(self.base, self.base.validate_framebuffer(), return);
2222
2223        let dst = match self.bound_pixel_pack_buffer.get() {
2224            Some(buffer) => buffer,
2225            None => return self.base.webgl_error(InvalidOperation),
2226        };
2227
2228        if dst_byte_offset < 0 {
2229            return self.base.webgl_error(InvalidValue);
2230        }
2231        let dst_byte_offset = dst_byte_offset as usize;
2232        if dst_byte_offset > dst.capacity() {
2233            return self.base.webgl_error(InvalidOperation);
2234        }
2235
2236        let ReadPixelsAllowedFormats {
2237            array_types: _,
2238            channels: bytes_per_pixel,
2239        } = match self.calc_read_pixel_formats(pixel_type, format) {
2240            Ok(result) => result,
2241            Err(error) => return self.base.webgl_error(error),
2242        };
2243        if format != constants::RGBA || pixel_type != constants::UNSIGNED_BYTE {
2244            return self.base.webgl_error(InvalidOperation);
2245        }
2246
2247        let ReadPixelsSizes {
2248            row_stride: _,
2249            skipped_bytes,
2250            size,
2251        } = match self.calc_read_pixel_sizes(width, height, bytes_per_pixel) {
2252            Ok(result) => result,
2253            Err(error) => return self.base.webgl_error(error),
2254        };
2255        let dst_end = dst_byte_offset + skipped_bytes + size;
2256        if dst.capacity() < dst_end {
2257            return self.base.webgl_error(InvalidOperation);
2258        }
2259
2260        {
2261            let (fb_width, fb_height) = handle_potential_webgl_error!(
2262                self.base,
2263                self.base
2264                    .get_current_framebuffer_size()
2265                    .ok_or(InvalidOperation),
2266                return
2267            );
2268            let src_origin = Point2D::new(x, y);
2269            let src_size = Size2D::new(width as u32, height as u32);
2270            let fb_size = Size2D::new(fb_width as u32, fb_height as u32);
2271            if pixels::clip(src_origin, src_size.to_u32(), fb_size.to_u32()).is_none() {
2272                return;
2273            }
2274        }
2275        let src_rect = Rect::new(Point2D::new(x, y), Size2D::new(width, height));
2276
2277        self.base.send_command(WebGLCommand::PixelStorei(
2278            constants::PACK_ALIGNMENT,
2279            self.base.get_texture_packing_alignment() as _,
2280        ));
2281        self.base.send_command(WebGLCommand::PixelStorei(
2282            constants::PACK_ROW_LENGTH,
2283            self.texture_pack_row_length.get() as _,
2284        ));
2285        self.base.send_command(WebGLCommand::PixelStorei(
2286            constants::PACK_SKIP_ROWS,
2287            self.texture_pack_skip_rows.get() as _,
2288        ));
2289        self.base.send_command(WebGLCommand::PixelStorei(
2290            constants::PACK_SKIP_PIXELS,
2291            self.texture_pack_skip_pixels.get() as _,
2292        ));
2293        self.base.send_command(WebGLCommand::ReadPixelsPP(
2294            src_rect,
2295            format,
2296            pixel_type,
2297            dst_byte_offset,
2298        ));
2299    }
2300
2301    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.10>
2302    fn ReadPixels__(
2303        &self,
2304        x: i32,
2305        y: i32,
2306        width: i32,
2307        height: i32,
2308        format: u32,
2309        pixel_type: u32,
2310        mut dst: CustomAutoRooterGuard<ArrayBufferView>,
2311        dst_elem_offset: u32,
2312    ) {
2313        self.read_pixels_into(
2314            x,
2315            y,
2316            width,
2317            height,
2318            format,
2319            pixel_type,
2320            &mut dst,
2321            dst_elem_offset,
2322        )
2323    }
2324
2325    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2326    fn SampleCoverage(&self, value: f32, invert: bool) {
2327        self.base.SampleCoverage(value, invert)
2328    }
2329
2330    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4>
2331    fn Scissor(&self, x: i32, y: i32, width: i32, height: i32) {
2332        self.base.Scissor(x, y, width, height)
2333    }
2334
2335    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2336    fn StencilFunc(&self, func: u32, ref_: i32, mask: u32) {
2337        self.base.StencilFunc(func, ref_, mask)
2338    }
2339
2340    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2341    fn StencilFuncSeparate(&self, face: u32, func: u32, ref_: i32, mask: u32) {
2342        self.base.StencilFuncSeparate(face, func, ref_, mask)
2343    }
2344
2345    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2346    fn StencilMask(&self, mask: u32) {
2347        self.base.StencilMask(mask)
2348    }
2349
2350    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2351    fn StencilMaskSeparate(&self, face: u32, mask: u32) {
2352        self.base.StencilMaskSeparate(face, mask)
2353    }
2354
2355    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2356    fn StencilOp(&self, fail: u32, zfail: u32, zpass: u32) {
2357        self.base.StencilOp(fail, zfail, zpass)
2358    }
2359
2360    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2361    fn StencilOpSeparate(&self, face: u32, fail: u32, zfail: u32, zpass: u32) {
2362        self.base.StencilOpSeparate(face, fail, zfail, zpass)
2363    }
2364
2365    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2366    fn LinkProgram(&self, program: &WebGLProgram) {
2367        self.base.LinkProgram(program)
2368    }
2369
2370    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2371    fn ShaderSource(&self, shader: &WebGLShader, source: DOMString) {
2372        self.base.ShaderSource(shader, source)
2373    }
2374
2375    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2376    fn GetShaderSource(&self, shader: &WebGLShader) -> Option<DOMString> {
2377        self.base.GetShaderSource(shader)
2378    }
2379
2380    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2381    fn Uniform1f(&self, location: Option<&WebGLUniformLocation>, val: f32) {
2382        self.base.Uniform1f(location, val)
2383    }
2384
2385    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2386    fn Uniform1i(&self, location: Option<&WebGLUniformLocation>, val: i32) {
2387        self.base.Uniform1i(location, val)
2388    }
2389
2390    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2391    fn Uniform1iv(
2392        &self,
2393        location: Option<&WebGLUniformLocation>,
2394        v: Int32ArrayOrLongSequence,
2395        src_offset: u32,
2396        src_length: u32,
2397    ) {
2398        self.base.uniform1iv(location, v, src_offset, src_length)
2399    }
2400
2401    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
2402    fn Uniform1ui(&self, location: Option<&WebGLUniformLocation>, val: u32) {
2403        self.base.with_location(location, |location| {
2404            match location.type_() {
2405                constants::BOOL | constants::UNSIGNED_INT => (),
2406                _ => return Err(InvalidOperation),
2407            }
2408            self.base
2409                .send_command(WebGLCommand::Uniform1ui(location.id(), val));
2410            Ok(())
2411        });
2412    }
2413
2414    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
2415    fn Uniform1uiv(
2416        &self,
2417        location: Option<&WebGLUniformLocation>,
2418        val: Uint32ArrayOrUnsignedLongSequence,
2419        src_offset: u32,
2420        src_length: u32,
2421    ) {
2422        self.base.with_location(location, |location| {
2423            match location.type_() {
2424                constants::BOOL |
2425                constants::UNSIGNED_INT |
2426                constants::SAMPLER_2D |
2427                constants::SAMPLER_2D_ARRAY |
2428                constants::SAMPLER_3D |
2429                constants::SAMPLER_CUBE => {},
2430                _ => return Err(InvalidOperation),
2431            }
2432
2433            let val = self.uniform_vec_section_uint(val, src_offset, src_length, 1, location)?;
2434
2435            match location.type_() {
2436                constants::SAMPLER_2D | constants::SAMPLER_CUBE => {
2437                    for &v in val
2438                        .iter()
2439                        .take(cmp::min(location.size().unwrap_or(1) as usize, val.len()))
2440                    {
2441                        if v >= self.base.limits().max_combined_texture_image_units {
2442                            return Err(InvalidValue);
2443                        }
2444                    }
2445                },
2446                _ => {},
2447            }
2448            self.base
2449                .send_command(WebGLCommand::Uniform1uiv(location.id(), val));
2450            Ok(())
2451        });
2452    }
2453
2454    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2455    fn Uniform1fv(
2456        &self,
2457        location: Option<&WebGLUniformLocation>,
2458        v: Float32ArrayOrUnrestrictedFloatSequence,
2459        src_offset: u32,
2460        src_length: u32,
2461    ) {
2462        self.base.uniform1fv(location, v, src_offset, src_length);
2463    }
2464
2465    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2466    fn Uniform2f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32) {
2467        self.base.Uniform2f(location, x, y)
2468    }
2469
2470    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2471    fn Uniform2fv(
2472        &self,
2473        location: Option<&WebGLUniformLocation>,
2474        v: Float32ArrayOrUnrestrictedFloatSequence,
2475        src_offset: u32,
2476        src_length: u32,
2477    ) {
2478        self.base.uniform2fv(location, v, src_offset, src_length);
2479    }
2480
2481    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2482    fn Uniform2i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32) {
2483        self.base.Uniform2i(location, x, y)
2484    }
2485
2486    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2487    fn Uniform2iv(
2488        &self,
2489        location: Option<&WebGLUniformLocation>,
2490        v: Int32ArrayOrLongSequence,
2491        src_offset: u32,
2492        src_length: u32,
2493    ) {
2494        self.base.uniform2iv(location, v, src_offset, src_length)
2495    }
2496
2497    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
2498    fn Uniform2ui(&self, location: Option<&WebGLUniformLocation>, x: u32, y: u32) {
2499        self.base.with_location(location, |location| {
2500            match location.type_() {
2501                constants::BOOL_VEC2 | constants::UNSIGNED_INT_VEC2 => {},
2502                _ => return Err(InvalidOperation),
2503            }
2504            self.base
2505                .send_command(WebGLCommand::Uniform2ui(location.id(), x, y));
2506            Ok(())
2507        });
2508    }
2509
2510    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
2511    fn Uniform2uiv(
2512        &self,
2513        location: Option<&WebGLUniformLocation>,
2514        val: Uint32ArrayOrUnsignedLongSequence,
2515        src_offset: u32,
2516        src_length: u32,
2517    ) {
2518        self.base.with_location(location, |location| {
2519            match location.type_() {
2520                constants::BOOL_VEC2 | constants::UNSIGNED_INT_VEC2 => {},
2521                _ => return Err(InvalidOperation),
2522            }
2523            let val = self.uniform_vec_section_uint(val, src_offset, src_length, 2, location)?;
2524            self.base
2525                .send_command(WebGLCommand::Uniform2uiv(location.id(), val));
2526            Ok(())
2527        });
2528    }
2529
2530    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2531    fn Uniform3f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32) {
2532        self.base.Uniform3f(location, x, y, z)
2533    }
2534
2535    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2536    fn Uniform3fv(
2537        &self,
2538        location: Option<&WebGLUniformLocation>,
2539        v: Float32ArrayOrUnrestrictedFloatSequence,
2540        src_offset: u32,
2541        src_length: u32,
2542    ) {
2543        self.base.uniform3fv(location, v, src_offset, src_length);
2544    }
2545
2546    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2547    fn Uniform3i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32) {
2548        self.base.Uniform3i(location, x, y, z)
2549    }
2550
2551    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2552    fn Uniform3iv(
2553        &self,
2554        location: Option<&WebGLUniformLocation>,
2555        v: Int32ArrayOrLongSequence,
2556        src_offset: u32,
2557        src_length: u32,
2558    ) {
2559        self.base.uniform3iv(location, v, src_offset, src_length)
2560    }
2561
2562    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
2563    fn Uniform3ui(&self, location: Option<&WebGLUniformLocation>, x: u32, y: u32, z: u32) {
2564        self.base.with_location(location, |location| {
2565            match location.type_() {
2566                constants::BOOL_VEC3 | constants::UNSIGNED_INT_VEC3 => {},
2567                _ => return Err(InvalidOperation),
2568            }
2569            self.base
2570                .send_command(WebGLCommand::Uniform3ui(location.id(), x, y, z));
2571            Ok(())
2572        });
2573    }
2574
2575    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
2576    fn Uniform3uiv(
2577        &self,
2578        location: Option<&WebGLUniformLocation>,
2579        val: Uint32ArrayOrUnsignedLongSequence,
2580        src_offset: u32,
2581        src_length: u32,
2582    ) {
2583        self.base.with_location(location, |location| {
2584            match location.type_() {
2585                constants::BOOL_VEC3 | constants::UNSIGNED_INT_VEC3 => {},
2586                _ => return Err(InvalidOperation),
2587            }
2588            let val = self.uniform_vec_section_uint(val, src_offset, src_length, 3, location)?;
2589            self.base
2590                .send_command(WebGLCommand::Uniform3uiv(location.id(), val));
2591            Ok(())
2592        });
2593    }
2594
2595    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2596    fn Uniform4i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32, w: i32) {
2597        self.base.Uniform4i(location, x, y, z, w)
2598    }
2599
2600    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2601    fn Uniform4iv(
2602        &self,
2603        location: Option<&WebGLUniformLocation>,
2604        v: Int32ArrayOrLongSequence,
2605        src_offset: u32,
2606        src_length: u32,
2607    ) {
2608        self.base.uniform4iv(location, v, src_offset, src_length)
2609    }
2610
2611    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
2612    fn Uniform4ui(&self, location: Option<&WebGLUniformLocation>, x: u32, y: u32, z: u32, w: u32) {
2613        self.base.with_location(location, |location| {
2614            match location.type_() {
2615                constants::BOOL_VEC4 | constants::UNSIGNED_INT_VEC4 => {},
2616                _ => return Err(InvalidOperation),
2617            }
2618            self.base
2619                .send_command(WebGLCommand::Uniform4ui(location.id(), x, y, z, w));
2620            Ok(())
2621        });
2622    }
2623
2624    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
2625    fn Uniform4uiv(
2626        &self,
2627        location: Option<&WebGLUniformLocation>,
2628        val: Uint32ArrayOrUnsignedLongSequence,
2629        src_offset: u32,
2630        src_length: u32,
2631    ) {
2632        self.base.with_location(location, |location| {
2633            match location.type_() {
2634                constants::BOOL_VEC4 | constants::UNSIGNED_INT_VEC4 => {},
2635                _ => return Err(InvalidOperation),
2636            }
2637            let val = self.uniform_vec_section_uint(val, src_offset, src_length, 4, location)?;
2638            self.base
2639                .send_command(WebGLCommand::Uniform4uiv(location.id(), val));
2640            Ok(())
2641        });
2642    }
2643
2644    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2645    fn Uniform4f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32, w: f32) {
2646        self.base.Uniform4f(location, x, y, z, w)
2647    }
2648
2649    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2650    fn Uniform4fv(
2651        &self,
2652        location: Option<&WebGLUniformLocation>,
2653        v: Float32ArrayOrUnrestrictedFloatSequence,
2654        src_offset: u32,
2655        src_length: u32,
2656    ) {
2657        self.base.uniform4fv(location, v, src_offset, src_length);
2658    }
2659
2660    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2661    fn UniformMatrix2fv(
2662        &self,
2663        location: Option<&WebGLUniformLocation>,
2664        transpose: bool,
2665        v: Float32ArrayOrUnrestrictedFloatSequence,
2666        src_offset: u32,
2667        src_length: u32,
2668    ) {
2669        self.base
2670            .uniform_matrix_2fv(location, transpose, v, src_offset, src_length)
2671    }
2672
2673    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2674    fn UniformMatrix3fv(
2675        &self,
2676        location: Option<&WebGLUniformLocation>,
2677        transpose: bool,
2678        v: Float32ArrayOrUnrestrictedFloatSequence,
2679        src_offset: u32,
2680        src_length: u32,
2681    ) {
2682        self.base
2683            .uniform_matrix_3fv(location, transpose, v, src_offset, src_length)
2684    }
2685
2686    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2687    fn UniformMatrix4fv(
2688        &self,
2689        location: Option<&WebGLUniformLocation>,
2690        transpose: bool,
2691        v: Float32ArrayOrUnrestrictedFloatSequence,
2692        src_offset: u32,
2693        src_length: u32,
2694    ) {
2695        self.base
2696            .uniform_matrix_4fv(location, transpose, v, src_offset, src_length)
2697    }
2698
2699    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
2700    fn UniformMatrix3x2fv(
2701        &self,
2702        location: Option<&WebGLUniformLocation>,
2703        transpose: bool,
2704        val: Float32ArrayOrUnrestrictedFloatSequence,
2705        src_offset: u32,
2706        src_length: u32,
2707    ) {
2708        self.base.with_location(location, |location| {
2709            match location.type_() {
2710                constants::FLOAT_MAT3x2 => {},
2711                _ => return Err(InvalidOperation),
2712            }
2713            let val = self.base.uniform_matrix_section(
2714                val,
2715                src_offset,
2716                src_length,
2717                transpose,
2718                3 * 2,
2719                location,
2720            )?;
2721            self.base
2722                .send_command(WebGLCommand::UniformMatrix3x2fv(location.id(), val));
2723            Ok(())
2724        });
2725    }
2726
2727    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
2728    fn UniformMatrix4x2fv(
2729        &self,
2730        location: Option<&WebGLUniformLocation>,
2731        transpose: bool,
2732        val: Float32ArrayOrUnrestrictedFloatSequence,
2733        src_offset: u32,
2734        src_length: u32,
2735    ) {
2736        self.base.with_location(location, |location| {
2737            match location.type_() {
2738                constants::FLOAT_MAT4x2 => {},
2739                _ => return Err(InvalidOperation),
2740            }
2741            let val = self.base.uniform_matrix_section(
2742                val,
2743                src_offset,
2744                src_length,
2745                transpose,
2746                4 * 2,
2747                location,
2748            )?;
2749            self.base
2750                .send_command(WebGLCommand::UniformMatrix4x2fv(location.id(), val));
2751            Ok(())
2752        });
2753    }
2754
2755    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
2756    fn UniformMatrix2x3fv(
2757        &self,
2758        location: Option<&WebGLUniformLocation>,
2759        transpose: bool,
2760        val: Float32ArrayOrUnrestrictedFloatSequence,
2761        src_offset: u32,
2762        src_length: u32,
2763    ) {
2764        self.base.with_location(location, |location| {
2765            match location.type_() {
2766                constants::FLOAT_MAT2x3 => {},
2767                _ => return Err(InvalidOperation),
2768            }
2769            let val = self.base.uniform_matrix_section(
2770                val,
2771                src_offset,
2772                src_length,
2773                transpose,
2774                2 * 3,
2775                location,
2776            )?;
2777            self.base
2778                .send_command(WebGLCommand::UniformMatrix2x3fv(location.id(), val));
2779            Ok(())
2780        });
2781    }
2782
2783    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
2784    fn UniformMatrix4x3fv(
2785        &self,
2786        location: Option<&WebGLUniformLocation>,
2787        transpose: bool,
2788        val: Float32ArrayOrUnrestrictedFloatSequence,
2789        src_offset: u32,
2790        src_length: u32,
2791    ) {
2792        self.base.with_location(location, |location| {
2793            match location.type_() {
2794                constants::FLOAT_MAT4x3 => {},
2795                _ => return Err(InvalidOperation),
2796            }
2797            let val = self.base.uniform_matrix_section(
2798                val,
2799                src_offset,
2800                src_length,
2801                transpose,
2802                4 * 3,
2803                location,
2804            )?;
2805            self.base
2806                .send_command(WebGLCommand::UniformMatrix4x3fv(location.id(), val));
2807            Ok(())
2808        });
2809    }
2810
2811    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
2812    fn UniformMatrix2x4fv(
2813        &self,
2814        location: Option<&WebGLUniformLocation>,
2815        transpose: bool,
2816        val: Float32ArrayOrUnrestrictedFloatSequence,
2817        src_offset: u32,
2818        src_length: u32,
2819    ) {
2820        self.base.with_location(location, |location| {
2821            match location.type_() {
2822                constants::FLOAT_MAT2x4 => {},
2823                _ => return Err(InvalidOperation),
2824            }
2825            let val = self.base.uniform_matrix_section(
2826                val,
2827                src_offset,
2828                src_length,
2829                transpose,
2830                2 * 4,
2831                location,
2832            )?;
2833            self.base
2834                .send_command(WebGLCommand::UniformMatrix2x4fv(location.id(), val));
2835            Ok(())
2836        });
2837    }
2838
2839    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
2840    fn UniformMatrix3x4fv(
2841        &self,
2842        location: Option<&WebGLUniformLocation>,
2843        transpose: bool,
2844        val: Float32ArrayOrUnrestrictedFloatSequence,
2845        src_offset: u32,
2846        src_length: u32,
2847    ) {
2848        self.base.with_location(location, |location| {
2849            match location.type_() {
2850                constants::FLOAT_MAT3x4 => {},
2851                _ => return Err(InvalidOperation),
2852            }
2853            let val = self.base.uniform_matrix_section(
2854                val,
2855                src_offset,
2856                src_length,
2857                transpose,
2858                3 * 4,
2859                location,
2860            )?;
2861            self.base
2862                .send_command(WebGLCommand::UniformMatrix3x4fv(location.id(), val));
2863            Ok(())
2864        });
2865    }
2866
2867    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
2868    #[expect(unsafe_code)]
2869    fn GetUniform(
2870        &self,
2871        cx: JSContext,
2872        program: &WebGLProgram,
2873        location: &WebGLUniformLocation,
2874        mut retval: MutableHandleValue,
2875    ) {
2876        handle_potential_webgl_error!(
2877            self.base,
2878            self.base.uniform_check_program(program, location),
2879            return retval.set(NullValue())
2880        );
2881
2882        let triple = (&*self.base, program.id(), location.id());
2883
2884        match location.type_() {
2885            constants::UNSIGNED_INT => retval.set(UInt32Value(uniform_get(
2886                triple,
2887                WebGLCommand::GetUniformUint,
2888            ))),
2889            constants::UNSIGNED_INT_VEC2 => unsafe {
2890                uniform_typed::<Uint32>(
2891                    *cx,
2892                    &uniform_get(triple, WebGLCommand::GetUniformUint2),
2893                    retval,
2894                )
2895            },
2896            constants::UNSIGNED_INT_VEC3 => unsafe {
2897                uniform_typed::<Uint32>(
2898                    *cx,
2899                    &uniform_get(triple, WebGLCommand::GetUniformUint3),
2900                    retval,
2901                )
2902            },
2903            constants::UNSIGNED_INT_VEC4 => unsafe {
2904                uniform_typed::<Uint32>(
2905                    *cx,
2906                    &uniform_get(triple, WebGLCommand::GetUniformUint4),
2907                    retval,
2908                )
2909            },
2910            constants::FLOAT_MAT2x3 => unsafe {
2911                uniform_typed::<Float32>(
2912                    *cx,
2913                    &uniform_get(triple, WebGLCommand::GetUniformFloat2x3),
2914                    retval,
2915                )
2916            },
2917            constants::FLOAT_MAT2x4 => unsafe {
2918                uniform_typed::<Float32>(
2919                    *cx,
2920                    &uniform_get(triple, WebGLCommand::GetUniformFloat2x4),
2921                    retval,
2922                )
2923            },
2924            constants::FLOAT_MAT3x2 => unsafe {
2925                uniform_typed::<Float32>(
2926                    *cx,
2927                    &uniform_get(triple, WebGLCommand::GetUniformFloat3x2),
2928                    retval,
2929                )
2930            },
2931            constants::FLOAT_MAT3x4 => unsafe {
2932                uniform_typed::<Float32>(
2933                    *cx,
2934                    &uniform_get(triple, WebGLCommand::GetUniformFloat3x4),
2935                    retval,
2936                )
2937            },
2938            constants::FLOAT_MAT4x2 => unsafe {
2939                uniform_typed::<Float32>(
2940                    *cx,
2941                    &uniform_get(triple, WebGLCommand::GetUniformFloat4x2),
2942                    retval,
2943                )
2944            },
2945            constants::FLOAT_MAT4x3 => unsafe {
2946                uniform_typed::<Float32>(
2947                    *cx,
2948                    &uniform_get(triple, WebGLCommand::GetUniformFloat4x3),
2949                    retval,
2950                )
2951            },
2952            constants::SAMPLER_3D | constants::SAMPLER_2D_ARRAY => {
2953                retval.set(Int32Value(uniform_get(triple, WebGLCommand::GetUniformInt)))
2954            },
2955            _ => self.base.GetUniform(cx, program, location, retval),
2956        }
2957    }
2958
2959    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2960    fn UseProgram(&self, program: Option<&WebGLProgram>) {
2961        self.base.UseProgram(program)
2962    }
2963
2964    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2965    fn ValidateProgram(&self, program: &WebGLProgram) {
2966        self.base.ValidateProgram(program)
2967    }
2968
2969    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2970    fn VertexAttrib1f(&self, indx: u32, x: f32) {
2971        self.base.VertexAttrib1f(indx, x)
2972    }
2973
2974    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2975    fn VertexAttrib1fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
2976        self.base.VertexAttrib1fv(indx, v)
2977    }
2978
2979    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2980    fn VertexAttrib2f(&self, indx: u32, x: f32, y: f32) {
2981        self.base.VertexAttrib2f(indx, x, y)
2982    }
2983
2984    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2985    fn VertexAttrib2fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
2986        self.base.VertexAttrib2fv(indx, v)
2987    }
2988
2989    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2990    fn VertexAttrib3f(&self, indx: u32, x: f32, y: f32, z: f32) {
2991        self.base.VertexAttrib3f(indx, x, y, z)
2992    }
2993
2994    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
2995    fn VertexAttrib3fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
2996        self.base.VertexAttrib3fv(indx, v)
2997    }
2998
2999    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3000    fn VertexAttrib4f(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) {
3001        self.base.VertexAttrib4f(indx, x, y, z, w)
3002    }
3003
3004    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3005    fn VertexAttrib4fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
3006        self.base.VertexAttrib4fv(indx, v)
3007    }
3008
3009    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
3010    fn VertexAttribI4i(&self, index: u32, x: i32, y: i32, z: i32, w: i32) {
3011        self.vertex_attrib_i(index, x, y, z, w)
3012    }
3013
3014    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
3015    fn VertexAttribI4iv(&self, index: u32, v: Int32ArrayOrLongSequence) {
3016        let values = match v {
3017            Int32ArrayOrLongSequence::Int32Array(v) => v.to_vec(),
3018            Int32ArrayOrLongSequence::LongSequence(v) => v,
3019        };
3020        if values.len() < 4 {
3021            return self.base.webgl_error(InvalidValue);
3022        }
3023        self.vertex_attrib_i(index, values[0], values[1], values[2], values[3]);
3024    }
3025
3026    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
3027    fn VertexAttribI4ui(&self, index: u32, x: u32, y: u32, z: u32, w: u32) {
3028        self.vertex_attrib_u(index, x, y, z, w)
3029    }
3030
3031    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
3032    fn VertexAttribI4uiv(&self, index: u32, v: Uint32ArrayOrUnsignedLongSequence) {
3033        let values = match v {
3034            Uint32ArrayOrUnsignedLongSequence::Uint32Array(v) => v.to_vec(),
3035            Uint32ArrayOrUnsignedLongSequence::UnsignedLongSequence(v) => v,
3036        };
3037        if values.len() < 4 {
3038            return self.base.webgl_error(InvalidValue);
3039        }
3040        self.vertex_attrib_u(index, values[0], values[1], values[2], values[3]);
3041    }
3042
3043    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3044    fn VertexAttribPointer(
3045        &self,
3046        attrib_id: u32,
3047        size: i32,
3048        data_type: u32,
3049        normalized: bool,
3050        stride: i32,
3051        offset: i64,
3052    ) {
3053        self.base
3054            .VertexAttribPointer(attrib_id, size, data_type, normalized, stride, offset)
3055    }
3056
3057    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.8>
3058    fn VertexAttribIPointer(&self, index: u32, size: i32, type_: u32, stride: i32, offset: i64) {
3059        match type_ {
3060            constants::BYTE |
3061            constants::UNSIGNED_BYTE |
3062            constants::SHORT |
3063            constants::UNSIGNED_SHORT |
3064            constants::INT |
3065            constants::UNSIGNED_INT => {},
3066            _ => return self.base.webgl_error(InvalidEnum),
3067        };
3068        self.base
3069            .VertexAttribPointer(index, size, type_, false, stride, offset)
3070    }
3071
3072    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4>
3073    fn Viewport(&self, x: i32, y: i32, width: i32, height: i32) {
3074        self.base.Viewport(x, y, width, height)
3075    }
3076
3077    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.6>
3078    ///
3079    /// Allocates and initializes the specified mipmap level of a three-dimensional or
3080    /// two-dimensional array texture.
3081    #[expect(unsafe_code)]
3082    fn TexImage3D(
3083        &self,
3084        target: u32,
3085        level: i32,
3086        internal_format: i32,
3087        width: i32,
3088        height: i32,
3089        depth: i32,
3090        border: i32,
3091        format: u32,
3092        type_: u32,
3093        src_data: CustomAutoRooterGuard<Option<ArrayBufferView>>,
3094    ) -> Fallible<()> {
3095        // If a WebGLBuffer is bound to the PIXEL_UNPACK_BUFFER target,
3096        // generates an INVALID_OPERATION error.
3097        if self.bound_pixel_unpack_buffer.get().is_some() {
3098            self.base.webgl_error(InvalidOperation);
3099            return Ok(());
3100        }
3101
3102        // If type is specified as FLOAT_32_UNSIGNED_INT_24_8_REV, srcData must be null;
3103        // otherwise, generates an INVALID_OPERATION error.
3104        if type_ == constants::FLOAT_32_UNSIGNED_INT_24_8_REV && src_data.is_some() {
3105            self.base.webgl_error(InvalidOperation);
3106            return Ok(());
3107        }
3108
3109        if border != 0 {
3110            self.base.webgl_error(InvalidValue);
3111            return Ok(());
3112        }
3113
3114        let Ok(TexImage3DValidatorResult {
3115            width,
3116            height,
3117            depth,
3118            level,
3119            border,
3120            texture,
3121            target,
3122            internal_format,
3123            format,
3124            data_type,
3125        }) = TexImage3DValidator::new(
3126            &self.base,
3127            target,
3128            level,
3129            internal_format as u32,
3130            width,
3131            height,
3132            depth,
3133            border,
3134            format,
3135            type_,
3136            &src_data,
3137        )
3138        .validate()
3139        else {
3140            return Ok(());
3141        };
3142
3143        // TODO: If pixel store parameter constraints are not met, generates an INVALID_OPERATION error.
3144
3145        // If srcData is null, a buffer of sufficient size initialized to 0 is passed.
3146        let unpacking_alignment = self.base.texture_unpacking_alignment();
3147        let buff = match *src_data {
3148            Some(ref data) => GenericSharedMemory::from_bytes(unsafe { data.as_slice() }),
3149            None => {
3150                let element_size = data_type.element_size();
3151                let components = format.components();
3152                let components_per_element = format.components();
3153                // FIXME: This is copied from tex_image_2d which is apparently incorrect
3154                // NOTE: width and height are positive or zero due to validate()
3155                let expected_byte_len = if height == 0 {
3156                    0
3157                } else {
3158                    // We need to be careful here to not count unpack
3159                    // alignment at the end of the image, otherwise (for
3160                    // example) passing a single byte for uploading a 1x1
3161                    // GL_ALPHA/GL_UNSIGNED_BYTE texture would throw an error.
3162                    let cpp = element_size * components / components_per_element;
3163                    let stride =
3164                        (width * cpp + unpacking_alignment - 1) & !(unpacking_alignment - 1);
3165                    stride * (height - 1) + width * cpp
3166                };
3167                GenericSharedMemory::from_bytes(&vec![0u8; expected_byte_len as usize])
3168            },
3169        };
3170        let (alpha_treatment, y_axis_treatment) =
3171            self.base.get_current_unpack_state(Alpha::NotPremultiplied);
3172        // If UNPACK_FLIP_Y_WEBGL or UNPACK_PREMULTIPLY_ALPHA_WEBGL is set to true, texImage3D and texSubImage3D
3173        // generate an INVALID_OPERATION error if they upload data from a PIXEL_UNPACK_BUFFER or a non-null client
3174        // side ArrayBufferView.
3175        if let (Some(AlphaTreatment::Premultiply), YAxisTreatment::Flipped) =
3176            (alpha_treatment, y_axis_treatment)
3177        {
3178            if src_data.is_some() {
3179                self.base.webgl_error(InvalidOperation);
3180                return Ok(());
3181            }
3182        }
3183        let tex_source = TexPixels::from_array(
3184            buff,
3185            Size2D::new(width, height),
3186            alpha_treatment,
3187            y_axis_treatment,
3188        );
3189
3190        self.tex_image_3d(
3191            &texture,
3192            target,
3193            data_type,
3194            internal_format,
3195            format,
3196            level,
3197            width,
3198            height,
3199            depth,
3200            border,
3201            unpacking_alignment,
3202            tex_source,
3203        );
3204        Ok(())
3205    }
3206
3207    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
3208    fn TexImage2D(
3209        &self,
3210        target: u32,
3211        level: i32,
3212        internal_format: i32,
3213        width: i32,
3214        height: i32,
3215        border: i32,
3216        format: u32,
3217        data_type: u32,
3218        pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
3219    ) -> Fallible<()> {
3220        self.base.TexImage2D(
3221            target,
3222            level,
3223            internal_format,
3224            width,
3225            height,
3226            border,
3227            format,
3228            data_type,
3229            pixels,
3230        )
3231    }
3232
3233    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
3234    fn TexImage2D_(
3235        &self,
3236        target: u32,
3237        level: i32,
3238        internal_format: i32,
3239        format: u32,
3240        data_type: u32,
3241        source: TexImageSource,
3242    ) -> ErrorResult {
3243        self.base
3244            .TexImage2D_(target, level, internal_format, format, data_type, source)
3245    }
3246
3247    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.6>
3248    fn TexImage2D__(
3249        &self,
3250        target: u32,
3251        level: i32,
3252        internalformat: i32,
3253        width: i32,
3254        height: i32,
3255        border: i32,
3256        format: u32,
3257        type_: u32,
3258        pbo_offset: i64,
3259    ) -> Fallible<()> {
3260        let pixel_unpack_buffer = match self.bound_pixel_unpack_buffer.get() {
3261            Some(pixel_unpack_buffer) => pixel_unpack_buffer,
3262            None => {
3263                self.base.webgl_error(InvalidOperation);
3264                return Ok(());
3265            },
3266        };
3267
3268        if let Some(tf_buffer) = self.bound_transform_feedback_buffer.get() {
3269            if pixel_unpack_buffer == tf_buffer {
3270                self.base.webgl_error(InvalidOperation);
3271                return Ok(());
3272            }
3273        }
3274
3275        if pbo_offset < 0 || pbo_offset as usize > pixel_unpack_buffer.capacity() {
3276            self.base.webgl_error(InvalidValue);
3277            return Ok(());
3278        }
3279
3280        let unpacking_alignment = self.base.texture_unpacking_alignment();
3281
3282        let validator = TexImage2DValidator::new(
3283            &self.base,
3284            target,
3285            level,
3286            internalformat as u32,
3287            width,
3288            height,
3289            border,
3290            format,
3291            type_,
3292        );
3293
3294        let TexImage2DValidatorResult {
3295            texture,
3296            target,
3297            width,
3298            height,
3299            level,
3300            border,
3301            internal_format,
3302            format,
3303            data_type,
3304        } = match validator.validate() {
3305            Ok(result) => result,
3306            Err(_) => return Ok(()),
3307        };
3308
3309        self.base.tex_image_2d(
3310            &texture,
3311            target,
3312            data_type,
3313            internal_format,
3314            format,
3315            level,
3316            border,
3317            unpacking_alignment,
3318            Size2D::new(width, height),
3319            TexSource::BufferOffset(pbo_offset),
3320        );
3321
3322        Ok(())
3323    }
3324
3325    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.6>
3326    fn TexImage2D___(
3327        &self,
3328        target: u32,
3329        level: i32,
3330        internalformat: i32,
3331        width: i32,
3332        height: i32,
3333        border: i32,
3334        format: u32,
3335        type_: u32,
3336        source: TexImageSource,
3337    ) -> Fallible<()> {
3338        if self.bound_pixel_unpack_buffer.get().is_some() {
3339            self.base.webgl_error(InvalidOperation);
3340            return Ok(());
3341        }
3342
3343        let validator = TexImage2DValidator::new(
3344            &self.base,
3345            target,
3346            level,
3347            internalformat as u32,
3348            width,
3349            height,
3350            border,
3351            format,
3352            type_,
3353        );
3354
3355        let TexImage2DValidatorResult {
3356            texture,
3357            target,
3358            width: _,
3359            height: _,
3360            level,
3361            border,
3362            internal_format,
3363            format,
3364            data_type,
3365        } = match validator.validate() {
3366            Ok(result) => result,
3367            Err(_) => return Ok(()),
3368        };
3369
3370        let unpacking_alignment = self.base.texture_unpacking_alignment();
3371
3372        let pixels = match self.base.get_image_pixels(source)? {
3373            Some(pixels) => pixels,
3374            None => return Ok(()),
3375        };
3376
3377        self.base.tex_image_2d(
3378            &texture,
3379            target,
3380            data_type,
3381            internal_format,
3382            format,
3383            level,
3384            border,
3385            unpacking_alignment,
3386            pixels.size(),
3387            TexSource::Pixels(pixels),
3388        );
3389
3390        Ok(())
3391    }
3392
3393    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.6>
3394    #[expect(unsafe_code)]
3395    fn TexImage2D____(
3396        &self,
3397        target: u32,
3398        level: i32,
3399        internalformat: i32,
3400        width: i32,
3401        height: i32,
3402        border: i32,
3403        format: u32,
3404        type_: u32,
3405        src_data: CustomAutoRooterGuard<ArrayBufferView>,
3406        src_offset: u32,
3407    ) -> Fallible<()> {
3408        if self.bound_pixel_unpack_buffer.get().is_some() {
3409            self.base.webgl_error(InvalidOperation);
3410            return Ok(());
3411        }
3412
3413        if type_ == constants::FLOAT_32_UNSIGNED_INT_24_8_REV {
3414            self.base.webgl_error(InvalidOperation);
3415            return Ok(());
3416        }
3417
3418        let validator = TexImage2DValidator::new(
3419            &self.base,
3420            target,
3421            level,
3422            internalformat as u32,
3423            width,
3424            height,
3425            border,
3426            format,
3427            type_,
3428        );
3429
3430        let TexImage2DValidatorResult {
3431            texture,
3432            target,
3433            width,
3434            height,
3435            level,
3436            border,
3437            internal_format,
3438            format,
3439            data_type,
3440        } = match validator.validate() {
3441            Ok(result) => result,
3442            Err(_) => return Ok(()),
3443        };
3444
3445        let unpacking_alignment = self.base.texture_unpacking_alignment();
3446
3447        let src_elem_size = src_data.get_array_type().byte_size().unwrap();
3448        let src_byte_offset = src_offset as usize * src_elem_size;
3449
3450        if src_data.len() <= src_byte_offset {
3451            self.base.webgl_error(InvalidOperation);
3452            return Ok(());
3453        }
3454
3455        let buff =
3456            GenericSharedMemory::from_bytes(unsafe { &src_data.as_slice()[src_byte_offset..] });
3457
3458        let expected_byte_length = match self.base.validate_tex_image_2d_data(
3459            width,
3460            height,
3461            format,
3462            data_type,
3463            unpacking_alignment,
3464            Some(&*src_data),
3465        ) {
3466            Ok(byte_length) => byte_length,
3467            Err(()) => return Ok(()),
3468        };
3469
3470        if expected_byte_length as usize > buff.len() {
3471            self.base.webgl_error(InvalidOperation);
3472            return Ok(());
3473        }
3474
3475        let size = Size2D::new(width, height);
3476
3477        let (alpha_treatment, y_axis_treatment) =
3478            self.base.get_current_unpack_state(Alpha::NotPremultiplied);
3479
3480        self.base.tex_image_2d(
3481            &texture,
3482            target,
3483            data_type,
3484            internal_format,
3485            format,
3486            level,
3487            border,
3488            unpacking_alignment,
3489            size,
3490            TexSource::Pixels(TexPixels::from_array(
3491                buff,
3492                size,
3493                alpha_treatment,
3494                y_axis_treatment,
3495            )),
3496        );
3497
3498        Ok(())
3499    }
3500
3501    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
3502    fn TexSubImage2D(
3503        &self,
3504        target: u32,
3505        level: i32,
3506        xoffset: i32,
3507        yoffset: i32,
3508        width: i32,
3509        height: i32,
3510        format: u32,
3511        data_type: u32,
3512        pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
3513    ) -> Fallible<()> {
3514        self.base.TexSubImage2D(
3515            target, level, xoffset, yoffset, width, height, format, data_type, pixels,
3516        )
3517    }
3518
3519    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
3520    fn TexSubImage2D_(
3521        &self,
3522        target: u32,
3523        level: i32,
3524        xoffset: i32,
3525        yoffset: i32,
3526        format: u32,
3527        data_type: u32,
3528        source: TexImageSource,
3529    ) -> ErrorResult {
3530        self.base
3531            .TexSubImage2D_(target, level, xoffset, yoffset, format, data_type, source)
3532    }
3533
3534    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
3535    fn TexParameterf(&self, target: u32, name: u32, value: f32) {
3536        self.base.TexParameterf(target, name, value)
3537    }
3538
3539    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
3540    fn TexParameteri(&self, target: u32, name: u32, value: i32) {
3541        self.base.TexParameteri(target, name, value)
3542    }
3543
3544    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
3545    fn CheckFramebufferStatus(&self, target: u32) -> u32 {
3546        let fb_slot = match target {
3547            constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
3548                self.base.get_draw_framebuffer_slot()
3549            },
3550            constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
3551            _ => {
3552                self.base.webgl_error(InvalidEnum);
3553                return 0;
3554            },
3555        };
3556        match fb_slot.get() {
3557            Some(fb) => fb.check_status(),
3558            None => constants::FRAMEBUFFER_COMPLETE,
3559        }
3560    }
3561
3562    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
3563    fn RenderbufferStorage(&self, target: u32, internal_format: u32, width: i32, height: i32) {
3564        self.base
3565            .RenderbufferStorage(target, internal_format, width, height)
3566    }
3567
3568    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4S>
3569    fn BlitFramebuffer(
3570        &self,
3571        src_x0: i32,
3572        src_y0: i32,
3573        src_x1: i32,
3574        src_y1: i32,
3575        dst_x0: i32,
3576        dst_y0: i32,
3577        dst_x1: i32,
3578        dst_y1: i32,
3579        mask: u32,
3580        filter: u32,
3581    ) {
3582        bitflags! {
3583            struct BlitFrameBufferFlags: u32 {
3584                const DEPTH = constants::DEPTH_BUFFER_BIT;
3585                const COLOR = constants::COLOR_BUFFER_BIT;
3586                const STENCIL = constants::STENCIL_BUFFER_BIT;
3587                const DEPTH_STENCIL = constants::DEPTH_BUFFER_BIT | constants::STENCIL_BUFFER_BIT;
3588            }
3589        };
3590        let Some(bits) = BlitFrameBufferFlags::from_bits(mask) else {
3591            return self.base.webgl_error(InvalidValue);
3592        };
3593        let attributes = self.base.GetContextAttributes().unwrap();
3594
3595        if bits.intersects(BlitFrameBufferFlags::DEPTH_STENCIL) {
3596            match filter {
3597                constants::LINEAR => return self.base.webgl_error(InvalidOperation),
3598                constants::NEAREST => {},
3599                _ => return self.base.webgl_error(InvalidOperation),
3600            }
3601        }
3602
3603        let src_fb = self.base.get_read_framebuffer_slot().get();
3604        let dst_fb = self.base.get_draw_framebuffer_slot().get();
3605
3606        let get_default_formats = || -> WebGLResult<(Option<u32>, Option<u32>, Option<u32>)> {
3607            // All attempts to blit to an antialiased back buffer should fail.
3608            if attributes.antialias {
3609                return Err(InvalidOperation);
3610            };
3611            let color = if attributes.alpha {
3612                Some(constants::RGBA8)
3613            } else {
3614                Some(constants::RGB8)
3615            };
3616            let (depth, stencil) = match (attributes.depth, attributes.stencil) {
3617                (true, true) => (
3618                    Some(constants::DEPTH24_STENCIL8),
3619                    Some(constants::DEPTH24_STENCIL8),
3620                ),
3621                (true, false) => (Some(constants::DEPTH_COMPONENT16), None),
3622                (false, true) => (None, Some(constants::STENCIL_INDEX8)),
3623                _ => (None, None),
3624            };
3625            Ok((color, depth, stencil))
3626        };
3627
3628        let (src_color, src_depth, src_stencil) = match src_fb {
3629            Some(fb) => {
3630                handle_potential_webgl_error!(self.base, fb.get_attachment_formats(), return)
3631            },
3632            None => handle_potential_webgl_error!(self.base, get_default_formats(), return),
3633        };
3634        let (dst_color, dst_depth, dst_stencil) = match dst_fb {
3635            Some(fb) => {
3636                handle_potential_webgl_error!(self.base, fb.get_attachment_formats(), return)
3637            },
3638            None => handle_potential_webgl_error!(self.base, get_default_formats(), return),
3639        };
3640
3641        if bits.intersects(BlitFrameBufferFlags::COLOR) && src_color != dst_color {
3642            return self.base.webgl_error(InvalidOperation);
3643        }
3644        if bits.intersects(BlitFrameBufferFlags::DEPTH) && src_depth != dst_depth {
3645            return self.base.webgl_error(InvalidOperation);
3646        }
3647        if bits.intersects(BlitFrameBufferFlags::STENCIL) && src_stencil != dst_stencil {
3648            return self.base.webgl_error(InvalidOperation);
3649        }
3650
3651        let src_width = src_x1.checked_sub(src_x0);
3652        let dst_width = dst_x1.checked_sub(dst_x0);
3653        let src_height = src_y1.checked_sub(src_y0);
3654        let dst_height = dst_y1.checked_sub(dst_y0);
3655
3656        if src_width.is_none() ||
3657            dst_width.is_none() ||
3658            src_height.is_none() ||
3659            dst_height.is_none()
3660        {
3661            return self.base.webgl_error(InvalidOperation);
3662        }
3663
3664        self.base.send_command(WebGLCommand::BlitFrameBuffer(
3665            src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter,
3666        ));
3667    }
3668
3669    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
3670    fn FramebufferRenderbuffer(
3671        &self,
3672        target: u32,
3673        attachment: u32,
3674        renderbuffertarget: u32,
3675        rb: Option<&WebGLRenderbuffer>,
3676    ) {
3677        if let Some(rb) = rb {
3678            handle_potential_webgl_error!(self.base, self.base.validate_ownership(rb), return);
3679        }
3680
3681        let fb_slot = match target {
3682            constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
3683                self.base.get_draw_framebuffer_slot()
3684            },
3685            constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
3686            _ => return self.base.webgl_error(InvalidEnum),
3687        };
3688
3689        if renderbuffertarget != constants::RENDERBUFFER {
3690            return self.base.webgl_error(InvalidEnum);
3691        }
3692
3693        match fb_slot.get() {
3694            Some(fb) => match attachment {
3695                constants::DEPTH_STENCIL_ATTACHMENT => {
3696                    handle_potential_webgl_error!(
3697                        self.base,
3698                        fb.renderbuffer(constants::DEPTH_ATTACHMENT, rb)
3699                    );
3700                    handle_potential_webgl_error!(
3701                        self.base,
3702                        fb.renderbuffer(constants::STENCIL_ATTACHMENT, rb)
3703                    );
3704                },
3705                _ => handle_potential_webgl_error!(self.base, fb.renderbuffer(attachment, rb)),
3706            },
3707            None => self.base.webgl_error(InvalidOperation),
3708        };
3709    }
3710
3711    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
3712    fn FramebufferTexture2D(
3713        &self,
3714        target: u32,
3715        attachment: u32,
3716        textarget: u32,
3717        texture: Option<&WebGLTexture>,
3718        level: i32,
3719    ) {
3720        if let Some(texture) = texture {
3721            handle_potential_webgl_error!(self.base, self.base.validate_ownership(texture), return);
3722        }
3723
3724        let fb_slot = match target {
3725            constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
3726                self.base.get_draw_framebuffer_slot()
3727            },
3728            constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
3729            _ => return self.base.webgl_error(InvalidEnum),
3730        };
3731        match fb_slot.get() {
3732            Some(fb) => handle_potential_webgl_error!(
3733                self.base,
3734                fb.texture2d(attachment, textarget, texture, level)
3735            ),
3736            None => self.base.webgl_error(InvalidOperation),
3737        }
3738    }
3739
3740    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
3741    fn GetAttachedShaders(&self, program: &WebGLProgram) -> Option<Vec<DomRoot<WebGLShader>>> {
3742        self.base.GetAttachedShaders(program)
3743    }
3744
3745    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.9>
3746    fn DrawArraysInstanced(&self, mode: u32, first: i32, count: i32, primcount: i32) {
3747        self.validate_uniform_block_for_draw();
3748        self.validate_vertex_attribs_for_draw();
3749        handle_potential_webgl_error!(
3750            self.base,
3751            self.base
3752                .draw_arrays_instanced(mode, first, count, primcount)
3753        )
3754    }
3755
3756    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.9>
3757    fn DrawElementsInstanced(
3758        &self,
3759        mode: u32,
3760        count: i32,
3761        type_: u32,
3762        offset: i64,
3763        primcount: i32,
3764    ) {
3765        self.validate_uniform_block_for_draw();
3766        self.validate_vertex_attribs_for_draw();
3767        handle_potential_webgl_error!(
3768            self.base,
3769            self.base
3770                .draw_elements_instanced(mode, count, type_, offset, primcount)
3771        )
3772    }
3773
3774    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.9>
3775    fn DrawRangeElements(
3776        &self,
3777        mode: u32,
3778        start: u32,
3779        end: u32,
3780        count: i32,
3781        type_: u32,
3782        offset: i64,
3783    ) {
3784        if end < start {
3785            self.base.webgl_error(InvalidValue);
3786            return;
3787        }
3788        self.validate_uniform_block_for_draw();
3789        self.validate_vertex_attribs_for_draw();
3790        handle_potential_webgl_error!(
3791            self.base,
3792            self.base
3793                .draw_elements_instanced(mode, count, type_, offset, 1)
3794        )
3795    }
3796
3797    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.9>
3798    fn VertexAttribDivisor(&self, index: u32, divisor: u32) {
3799        self.base.vertex_attrib_divisor(index, divisor);
3800    }
3801
3802    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12>
3803    fn CreateQuery(&self) -> Option<DomRoot<WebGLQuery>> {
3804        Some(WebGLQuery::new(&self.base, CanGc::note()))
3805    }
3806
3807    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12>
3808    #[rustfmt::skip]
3809    fn DeleteQuery(&self, query: Option<&WebGLQuery>) {
3810        if let Some(query) = query {
3811            handle_potential_webgl_error!(self.base, self.base.validate_ownership(query), return);
3812
3813            if let Some(query_target) = query.target() {
3814                let slot = match query_target {
3815                    constants::ANY_SAMPLES_PASSED |
3816                    constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
3817                        &self.occlusion_query
3818                    },
3819                    constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
3820                        &self.primitives_query
3821                    },
3822                    _ => unreachable!(),
3823                };
3824                if let Some(stored_query) = slot.get() {
3825                    if stored_query.target() == query.target() {
3826                        slot.set(None);
3827                    }
3828                }
3829            }
3830
3831            query.delete(Operation::Infallible);
3832        }
3833    }
3834
3835    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12>
3836    fn IsQuery(&self, query: Option<&WebGLQuery>) -> bool {
3837        match query {
3838            Some(query) => self.base.validate_ownership(query).is_ok() && query.is_valid(),
3839            None => false,
3840        }
3841    }
3842
3843    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13>
3844    fn CreateSampler(&self) -> Option<DomRoot<WebGLSampler>> {
3845        Some(WebGLSampler::new(&self.base, CanGc::note()))
3846    }
3847
3848    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13>
3849    fn DeleteSampler(&self, sampler: Option<&WebGLSampler>) {
3850        if let Some(sampler) = sampler {
3851            handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
3852            for slot in self.samplers.iter() {
3853                if slot.get().is_some_and(|s| sampler == &*s) {
3854                    slot.set(None);
3855                }
3856            }
3857            sampler.delete(Operation::Infallible);
3858        }
3859    }
3860
3861    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13>
3862    fn IsSampler(&self, sampler: Option<&WebGLSampler>) -> bool {
3863        match sampler {
3864            Some(sampler) => self.base.validate_ownership(sampler).is_ok() && sampler.is_valid(),
3865            None => false,
3866        }
3867    }
3868
3869    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12>
3870    #[rustfmt::skip]
3871    fn BeginQuery(&self, target: u32, query: &WebGLQuery) {
3872        handle_potential_webgl_error!(self.base, self.base.validate_ownership(query), return);
3873
3874        let active_query = match target {
3875            constants::ANY_SAMPLES_PASSED |
3876            constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
3877                &self.occlusion_query
3878            },
3879            constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
3880                &self.primitives_query
3881            },
3882            _ => {
3883                self.base.webgl_error(InvalidEnum);
3884                return;
3885            },
3886        };
3887        if active_query.get().is_some() {
3888            self.base.webgl_error(InvalidOperation);
3889            return;
3890        }
3891        let result = query.begin(&self.base, target);
3892        match result {
3893            Ok(_) => active_query.set(Some(query)),
3894            Err(error) => self.base.webgl_error(error),
3895        }
3896    }
3897
3898    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12>
3899    #[rustfmt::skip]
3900    fn EndQuery(&self, target: u32) {
3901        let active_query = match target {
3902            constants::ANY_SAMPLES_PASSED |
3903            constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
3904                self.occlusion_query.take()
3905            },
3906            constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
3907                self.primitives_query.take()
3908            },
3909            _ => {
3910                self.base.webgl_error(InvalidEnum);
3911                return;
3912            },
3913        };
3914        match active_query {
3915            None => self.base.webgl_error(InvalidOperation),
3916            Some(query) => {
3917                let result = query.end(&self.base, target);
3918                if let Err(error) = result {
3919                    self.base.webgl_error(error);
3920                }
3921            },
3922        }
3923    }
3924
3925    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12>
3926    #[rustfmt::skip]
3927    fn GetQuery(&self, target: u32, pname: u32) -> Option<DomRoot<WebGLQuery>> {
3928        if pname != constants::CURRENT_QUERY {
3929            self.base.webgl_error(InvalidEnum);
3930            return None;
3931        }
3932        let active_query = match target {
3933            constants::ANY_SAMPLES_PASSED |
3934            constants::ANY_SAMPLES_PASSED_CONSERVATIVE => {
3935                self.occlusion_query.get()
3936            },
3937            constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => {
3938                self.primitives_query.get()
3939            },
3940            _ => {
3941                self.base.webgl_error(InvalidEnum);
3942                None
3943            },
3944        };
3945        if let Some(query) = active_query.as_ref() {
3946            if query.target() != Some(target) {
3947                return None;
3948            }
3949        }
3950        active_query
3951    }
3952
3953    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12>
3954    #[rustfmt::skip]
3955    fn GetQueryParameter(&self, _cx: JSContext, query: &WebGLQuery, pname: u32, mut retval: MutableHandleValue) {
3956        handle_potential_webgl_error!(
3957            self.base,
3958            self.base.validate_ownership(query),
3959            return retval.set(NullValue())
3960        );
3961        match query.get_parameter(&self.base, pname) {
3962            Ok(value) => match pname {
3963                constants::QUERY_RESULT => retval.set(UInt32Value(value)),
3964                constants::QUERY_RESULT_AVAILABLE => retval.set(BooleanValue(value != 0)),
3965                _ => unreachable!(),
3966            },
3967            Err(error) => {
3968                self.base.webgl_error(error);
3969                retval.set(NullValue())
3970            },
3971        }
3972    }
3973
3974    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.14>
3975    fn FenceSync(&self, condition: u32, flags: u32) -> Option<DomRoot<WebGLSync>> {
3976        if flags != 0 {
3977            self.base.webgl_error(InvalidValue);
3978            return None;
3979        }
3980        if condition != constants::SYNC_GPU_COMMANDS_COMPLETE {
3981            self.base.webgl_error(InvalidEnum);
3982            return None;
3983        }
3984
3985        Some(WebGLSync::new(&self.base, CanGc::note()))
3986    }
3987
3988    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.14>
3989    fn IsSync(&self, sync: Option<&WebGLSync>) -> bool {
3990        match sync {
3991            Some(sync) => {
3992                // Returns false if the sync was generated by a different
3993                // WebGL2RenderingContext than this one.
3994                if self.base.validate_ownership(sync).is_err() {
3995                    return false;
3996                }
3997
3998                // Returns false if the sync's invalidated flag is set.
3999                if !sync.is_valid() {
4000                    return false;
4001                }
4002
4003                // Return true if the passed WebGLSync is valid and false otherwise.
4004                handle_potential_webgl_error!(
4005                    self.base,
4006                    self.base.validate_ownership(sync),
4007                    return false
4008                );
4009                let (sender, receiver) = webgl_channel().unwrap();
4010                self.base
4011                    .send_command(WebGLCommand::IsSync(sync.id(), sender));
4012                receiver.recv().unwrap()
4013            },
4014            None => false,
4015        }
4016    }
4017
4018    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.14>
4019    fn ClientWaitSync(&self, sync: &WebGLSync, flags: u32, timeout: u64) -> u32 {
4020        if !sync.is_valid() {
4021            self.base.webgl_error(InvalidOperation);
4022            return constants::WAIT_FAILED;
4023        }
4024        handle_potential_webgl_error!(
4025            self.base,
4026            self.base.validate_ownership(sync),
4027            return constants::WAIT_FAILED
4028        );
4029        if flags != 0 && flags != constants::SYNC_FLUSH_COMMANDS_BIT {
4030            self.base.webgl_error(InvalidValue);
4031            return constants::WAIT_FAILED;
4032        }
4033        if timeout > self.base.limits().max_client_wait_timeout_webgl.as_nanos() as u64 {
4034            self.base.webgl_error(InvalidOperation);
4035            return constants::WAIT_FAILED;
4036        }
4037
4038        match sync.client_wait_sync(&self.base, flags, timeout) {
4039            Some(status) => status,
4040            None => constants::WAIT_FAILED,
4041        }
4042    }
4043
4044    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.14>
4045    fn WaitSync(&self, sync: &WebGLSync, flags: u32, timeout: i64) {
4046        if !sync.is_valid() {
4047            self.base.webgl_error(InvalidOperation);
4048            return;
4049        }
4050        handle_potential_webgl_error!(self.base, self.base.validate_ownership(sync), return);
4051        if flags != 0 {
4052            self.base.webgl_error(InvalidValue);
4053            return;
4054        }
4055        if timeout != constants::TIMEOUT_IGNORED {
4056            self.base.webgl_error(InvalidValue);
4057            return;
4058        }
4059
4060        self.base
4061            .send_command(WebGLCommand::WaitSync(sync.id(), flags, timeout));
4062    }
4063
4064    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.14>
4065    fn GetSyncParameter(
4066        &self,
4067        _cx: JSContext,
4068        sync: &WebGLSync,
4069        pname: u32,
4070        mut retval: MutableHandleValue,
4071    ) {
4072        if !sync.is_valid() {
4073            self.base.webgl_error(InvalidOperation);
4074            return retval.set(NullValue());
4075        }
4076        handle_potential_webgl_error!(
4077            self.base,
4078            self.base.validate_ownership(sync),
4079            return retval.set(NullValue())
4080        );
4081        match pname {
4082            constants::OBJECT_TYPE | constants::SYNC_CONDITION | constants::SYNC_FLAGS => {
4083                let (sender, receiver) = webgl_channel().unwrap();
4084                self.base
4085                    .send_command(WebGLCommand::GetSyncParameter(sync.id(), pname, sender));
4086                retval.set(UInt32Value(receiver.recv().unwrap()))
4087            },
4088            constants::SYNC_STATUS => match sync.get_sync_status(pname, &self.base) {
4089                Some(status) => retval.set(UInt32Value(status)),
4090                None => retval.set(UInt32Value(constants::UNSIGNALED)),
4091            },
4092            _ => {
4093                self.base.webgl_error(InvalidEnum);
4094                retval.set(NullValue())
4095            },
4096        }
4097    }
4098
4099    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.14>
4100    fn DeleteSync(&self, sync: Option<&WebGLSync>) {
4101        if let Some(sync) = sync {
4102            handle_potential_webgl_error!(self.base, self.base.validate_ownership(sync), return);
4103            sync.delete(Operation::Infallible);
4104        }
4105    }
4106
4107    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13>
4108    fn BindSampler(&self, unit: u32, sampler: Option<&WebGLSampler>) {
4109        if let Some(sampler) = sampler {
4110            handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
4111
4112            if unit as usize >= self.samplers.len() {
4113                self.base.webgl_error(InvalidValue);
4114                return;
4115            }
4116
4117            let result = sampler.bind(&self.base, unit);
4118            match result {
4119                Ok(_) => self.samplers[unit as usize].set(Some(sampler)),
4120                Err(error) => self.base.webgl_error(error),
4121            }
4122        }
4123    }
4124
4125    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17>
4126    fn BindVertexArray(&self, array: Option<&WebGLVertexArrayObject>) {
4127        self.base.bind_vertex_array_webgl2(array);
4128    }
4129
4130    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13>
4131    fn SamplerParameteri(&self, sampler: &WebGLSampler, pname: u32, param: i32) {
4132        handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
4133        let param = WebGLSamplerValue::GLenum(param as u32);
4134        let result = sampler.set_parameter(&self.base, pname, param);
4135        if let Err(error) = result {
4136            self.base.webgl_error(error);
4137        }
4138    }
4139
4140    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13>
4141    fn SamplerParameterf(&self, sampler: &WebGLSampler, pname: u32, param: f32) {
4142        handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return);
4143        let param = WebGLSamplerValue::Float(param);
4144        let result = sampler.set_parameter(&self.base, pname, param);
4145        if let Err(error) = result {
4146            self.base.webgl_error(error);
4147        }
4148    }
4149
4150    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13>
4151    fn GetSamplerParameter(
4152        &self,
4153        _cx: JSContext,
4154        sampler: &WebGLSampler,
4155        pname: u32,
4156        mut retval: MutableHandleValue,
4157    ) {
4158        handle_potential_webgl_error!(
4159            self.base,
4160            self.base.validate_ownership(sampler),
4161            return retval.set(NullValue())
4162        );
4163        match sampler.get_parameter(&self.base, pname) {
4164            Ok(value) => match value {
4165                WebGLSamplerValue::GLenum(value) => retval.set(UInt32Value(value)),
4166                WebGLSamplerValue::Float(value) => retval.set(DoubleValue(value as f64)),
4167            },
4168            Err(error) => {
4169                self.base.webgl_error(error);
4170                retval.set(NullValue())
4171            },
4172        }
4173    }
4174
4175    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15>
4176    fn CreateTransformFeedback(&self) -> Option<DomRoot<WebGLTransformFeedback>> {
4177        Some(WebGLTransformFeedback::new(&self.base, CanGc::note()))
4178    }
4179
4180    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15>
4181    fn DeleteTransformFeedback(&self, tf: Option<&WebGLTransformFeedback>) {
4182        if let Some(tf) = tf {
4183            handle_potential_webgl_error!(self.base, self.base.validate_ownership(tf), return);
4184            if tf.is_active() {
4185                self.base.webgl_error(InvalidOperation);
4186                return;
4187            }
4188            tf.delete(Operation::Infallible);
4189            self.current_transform_feedback.set(None);
4190        }
4191    }
4192
4193    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15>
4194    fn IsTransformFeedback(&self, tf: Option<&WebGLTransformFeedback>) -> bool {
4195        match tf {
4196            Some(tf) => {
4197                // Returns false if the transform feedback was generated by a different
4198                // WebGL2RenderingContext than this one.
4199                if self.base.validate_ownership(tf).is_err() {
4200                    return false;
4201                }
4202
4203                // Returns false if the transform feedback's invalidated flag is set.
4204                if !tf.is_valid() {
4205                    return false;
4206                }
4207
4208                // Return true if the passed WebGLTransformFeedback is valid and false otherwise.
4209                handle_potential_webgl_error!(
4210                    self.base,
4211                    self.base.validate_ownership(tf),
4212                    return false
4213                );
4214                let (sender, receiver) = webgl_channel().unwrap();
4215                self.base
4216                    .send_command(WebGLCommand::IsTransformFeedback(tf.id(), sender));
4217                receiver.recv().unwrap()
4218            },
4219            // Returns false if `transformFeedback` is null.
4220            None => false,
4221        }
4222    }
4223
4224    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15>
4225    fn BindTransformFeedback(&self, target: u32, tf: Option<&WebGLTransformFeedback>) {
4226        if target != constants::TRANSFORM_FEEDBACK {
4227            self.base.webgl_error(InvalidEnum);
4228            return;
4229        }
4230        match tf {
4231            Some(transform_feedback) => {
4232                handle_potential_webgl_error!(
4233                    self.base,
4234                    self.base.validate_ownership(transform_feedback),
4235                    return
4236                );
4237                if !transform_feedback.is_valid() {
4238                    self.base.webgl_error(InvalidOperation);
4239                    return;
4240                }
4241                if let Some(current_tf) = self.current_transform_feedback.get() {
4242                    if current_tf.is_active() && !current_tf.is_paused() {
4243                        self.base.webgl_error(InvalidOperation);
4244                        return;
4245                    }
4246                }
4247                transform_feedback.bind(&self.base, target);
4248                self.current_transform_feedback
4249                    .set(Some(transform_feedback));
4250            },
4251            None => self
4252                .base
4253                .send_command(WebGLCommand::BindTransformFeedback(target, 0)),
4254        }
4255    }
4256
4257    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15>
4258    #[expect(non_snake_case)]
4259    fn BeginTransformFeedback(&self, primitiveMode: u32) {
4260        match primitiveMode {
4261            constants::POINTS | constants::LINES | constants::TRIANGLES => {},
4262            _ => {
4263                self.base.webgl_error(InvalidEnum);
4264                return;
4265            },
4266        };
4267        let current_tf = match self.current_transform_feedback.get() {
4268            Some(current_tf) => current_tf,
4269            None => {
4270                self.base.webgl_error(InvalidOperation);
4271                return;
4272            },
4273        };
4274        if current_tf.is_active() {
4275            self.base.webgl_error(InvalidOperation);
4276            return;
4277        };
4278        let program = match self.base.current_program() {
4279            Some(program) => program,
4280            None => {
4281                self.base.webgl_error(InvalidOperation);
4282                return;
4283            },
4284        };
4285        if !program.is_linked() || program.transform_feedback_varyings_length() == 0 {
4286            self.base.webgl_error(InvalidOperation);
4287            return;
4288        };
4289        current_tf.begin(&self.base, primitiveMode);
4290    }
4291
4292    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15>
4293    fn EndTransformFeedback(&self) {
4294        if let Some(current_tf) = self.current_transform_feedback.get() {
4295            if !current_tf.is_active() {
4296                self.base.webgl_error(InvalidOperation);
4297                return;
4298            }
4299            current_tf.end(&self.base);
4300        }
4301    }
4302
4303    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15>
4304    fn ResumeTransformFeedback(&self) {
4305        if let Some(current_tf) = self.current_transform_feedback.get() {
4306            if !current_tf.is_active() || !current_tf.is_paused() {
4307                self.base.webgl_error(InvalidOperation);
4308                return;
4309            }
4310            current_tf.resume(&self.base);
4311        }
4312    }
4313
4314    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15>
4315    fn PauseTransformFeedback(&self) {
4316        if let Some(current_tf) = self.current_transform_feedback.get() {
4317            if !current_tf.is_active() || current_tf.is_paused() {
4318                self.base.webgl_error(InvalidOperation);
4319                return;
4320            }
4321            current_tf.pause(&self.base);
4322        }
4323    }
4324
4325    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15>
4326    #[expect(non_snake_case)]
4327    fn TransformFeedbackVaryings(
4328        &self,
4329        program: &WebGLProgram,
4330        varyings: Vec<DOMString>,
4331        bufferMode: u32,
4332    ) {
4333        // Spec: If program was generated by a different WebGL2RenderingContext
4334        // than this one, generates an INVALID_OPERATION error.
4335        if let Err(error) = self.base.validate_ownership(program) {
4336            self.base.webgl_error(error);
4337            return;
4338        }
4339        handle_potential_webgl_error!(self.base, program.validate(), return);
4340        let strs = varyings
4341            .iter()
4342            .map(|name| String::from(name.to_owned()))
4343            .collect::<Vec<String>>();
4344        match bufferMode {
4345            constants::INTERLEAVED_ATTRIBS => {
4346                self.base
4347                    .send_command(WebGLCommand::TransformFeedbackVaryings(
4348                        program.id(),
4349                        strs,
4350                        bufferMode,
4351                    ));
4352            },
4353            constants::SEPARATE_ATTRIBS => {
4354                let max_tf_sp_att =
4355                    self.base.limits().max_transform_feedback_separate_attribs as usize;
4356                if strs.len() >= max_tf_sp_att {
4357                    self.base.webgl_error(InvalidValue);
4358                    return;
4359                }
4360                self.base
4361                    .send_command(WebGLCommand::TransformFeedbackVaryings(
4362                        program.id(),
4363                        strs,
4364                        bufferMode,
4365                    ));
4366            },
4367            _ => self.base.webgl_error(InvalidEnum),
4368        }
4369    }
4370
4371    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.15>
4372    fn GetTransformFeedbackVarying(
4373        &self,
4374        program: &WebGLProgram,
4375        index: u32,
4376    ) -> Option<DomRoot<WebGLActiveInfo>> {
4377        // Spec: If program was generated by a different WebGL2RenderingContext,
4378        // generate INVALID_OPERATION and return null.
4379        if let Err(error) = self.base.validate_ownership(program) {
4380            self.base.webgl_error(error);
4381            return None;
4382        }
4383
4384        handle_potential_webgl_error!(self.base, program.validate(), return None);
4385
4386        if index >= program.transform_feedback_varyings_length() as u32 {
4387            self.base.webgl_error(InvalidValue);
4388            return None;
4389        }
4390
4391        let (sender, receiver) = webgl_channel().unwrap();
4392        self.base
4393            .send_command(WebGLCommand::GetTransformFeedbackVarying(
4394                program.id(),
4395                index,
4396                sender,
4397            ));
4398        let (size, ty, name) = receiver.recv().unwrap();
4399        Some(WebGLActiveInfo::new(
4400            self.base.global().as_window(),
4401            size,
4402            ty,
4403            DOMString::from(name),
4404            CanGc::note(),
4405        ))
4406    }
4407
4408    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16>
4409    fn BindBufferBase(&self, target: u32, index: u32, buffer: Option<&WebGLBuffer>) {
4410        let (generic_slot, indexed_bindings) = match target {
4411            constants::TRANSFORM_FEEDBACK_BUFFER => (
4412                &self.bound_transform_feedback_buffer,
4413                &self.indexed_transform_feedback_buffer_bindings,
4414            ),
4415            constants::UNIFORM_BUFFER => (
4416                &self.bound_uniform_buffer,
4417                &self.indexed_uniform_buffer_bindings,
4418            ),
4419            _ => return self.base.webgl_error(InvalidEnum),
4420        };
4421        let indexed_binding = match indexed_bindings.get(index as usize) {
4422            Some(slot) => slot,
4423            None => return self.base.webgl_error(InvalidValue),
4424        };
4425
4426        if let Some(buffer) = buffer {
4427            handle_potential_webgl_error!(self.base, self.base.validate_ownership(buffer), return);
4428
4429            if buffer.is_marked_for_deletion() {
4430                return self.base.webgl_error(InvalidOperation);
4431            }
4432            handle_potential_webgl_error!(self.base, buffer.set_target_maybe(target), return);
4433
4434            // for both the generic and the indexed bindings
4435            buffer.increment_attached_counter();
4436            buffer.increment_attached_counter();
4437        }
4438
4439        self.base.send_command(WebGLCommand::BindBufferBase(
4440            target,
4441            index,
4442            buffer.map(|b| b.id()),
4443        ));
4444
4445        for slot in &[generic_slot, &indexed_binding.buffer] {
4446            if let Some(old) = slot.get() {
4447                old.decrement_attached_counter(Operation::Infallible);
4448            }
4449            slot.set(buffer);
4450        }
4451        indexed_binding.start.set(0);
4452        indexed_binding.size.set(0);
4453    }
4454
4455    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16>
4456    fn BindBufferRange(
4457        &self,
4458        target: u32,
4459        index: u32,
4460        buffer: Option<&WebGLBuffer>,
4461        offset: i64,
4462        size: i64,
4463    ) {
4464        let (generic_slot, indexed_bindings) = match target {
4465            constants::TRANSFORM_FEEDBACK_BUFFER => (
4466                &self.bound_transform_feedback_buffer,
4467                &self.indexed_transform_feedback_buffer_bindings,
4468            ),
4469            constants::UNIFORM_BUFFER => (
4470                &self.bound_uniform_buffer,
4471                &self.indexed_uniform_buffer_bindings,
4472            ),
4473            _ => return self.base.webgl_error(InvalidEnum),
4474        };
4475        let indexed_binding = match indexed_bindings.get(index as usize) {
4476            Some(slot) => slot,
4477            None => return self.base.webgl_error(InvalidValue),
4478        };
4479
4480        if offset < 0 || size < 0 {
4481            return self.base.webgl_error(InvalidValue);
4482        }
4483        if buffer.is_some() && size == 0 {
4484            return self.base.webgl_error(InvalidValue);
4485        }
4486
4487        match target {
4488            constants::TRANSFORM_FEEDBACK_BUFFER => {
4489                if size % 4 != 0 && offset % 4 != 0 {
4490                    return self.base.webgl_error(InvalidValue);
4491                }
4492            },
4493            constants::UNIFORM_BUFFER => {
4494                let offset_alignment = self.base.limits().uniform_buffer_offset_alignment;
4495                if offset % offset_alignment as i64 != 0 {
4496                    return self.base.webgl_error(InvalidValue);
4497                }
4498            },
4499            _ => unreachable!(),
4500        }
4501
4502        if let Some(buffer) = buffer {
4503            handle_potential_webgl_error!(self.base, self.base.validate_ownership(buffer), return);
4504
4505            if buffer.is_marked_for_deletion() {
4506                return self.base.webgl_error(InvalidOperation);
4507            }
4508            handle_potential_webgl_error!(self.base, buffer.set_target_maybe(target), return);
4509
4510            // for both the generic and the indexed bindings
4511            buffer.increment_attached_counter();
4512            buffer.increment_attached_counter();
4513        }
4514
4515        self.base.send_command(WebGLCommand::BindBufferRange(
4516            target,
4517            index,
4518            buffer.map(|b| b.id()),
4519            offset,
4520            size,
4521        ));
4522
4523        for slot in &[generic_slot, &indexed_binding.buffer] {
4524            if let Some(old) = slot.get() {
4525                old.decrement_attached_counter(Operation::Infallible);
4526            }
4527            slot.set(buffer);
4528        }
4529        indexed_binding.start.set(offset);
4530        indexed_binding.size.set(size);
4531    }
4532
4533    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16>
4534    fn GetUniformIndices(&self, program: &WebGLProgram, names: Vec<DOMString>) -> Option<Vec<u32>> {
4535        handle_potential_webgl_error!(
4536            self.base,
4537            self.base.validate_ownership(program),
4538            return None
4539        );
4540        let indices = handle_potential_webgl_error!(
4541            self.base,
4542            program.get_uniform_indices(names),
4543            return None
4544        );
4545        Some(indices)
4546    }
4547
4548    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16>
4549    fn GetActiveUniforms(
4550        &self,
4551        cx: JSContext,
4552        program: &WebGLProgram,
4553        indices: Vec<u32>,
4554        pname: u32,
4555        mut rval: MutableHandleValue,
4556    ) {
4557        handle_potential_webgl_error!(
4558            self.base,
4559            self.base.validate_ownership(program),
4560            return rval.set(NullValue())
4561        );
4562        let values = handle_potential_webgl_error!(
4563            self.base,
4564            program.get_active_uniforms(indices, pname),
4565            return rval.set(NullValue())
4566        );
4567
4568        match pname {
4569            constants::UNIFORM_SIZE |
4570            constants::UNIFORM_TYPE |
4571            constants::UNIFORM_BLOCK_INDEX |
4572            constants::UNIFORM_OFFSET |
4573            constants::UNIFORM_ARRAY_STRIDE |
4574            constants::UNIFORM_MATRIX_STRIDE => {
4575                values.safe_to_jsval(cx, rval, CanGc::note());
4576            },
4577            constants::UNIFORM_IS_ROW_MAJOR => {
4578                let values = values.iter().map(|&v| v != 0).collect::<Vec<_>>();
4579                values.safe_to_jsval(cx, rval, CanGc::note());
4580            },
4581            _ => unreachable!(),
4582        }
4583    }
4584
4585    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16>
4586    fn GetUniformBlockIndex(&self, program: &WebGLProgram, block_name: DOMString) -> u32 {
4587        handle_potential_webgl_error!(
4588            self.base,
4589            self.base.validate_ownership(program),
4590            return constants::INVALID_INDEX
4591        );
4592        handle_potential_webgl_error!(
4593            self.base,
4594            program.get_uniform_block_index(block_name),
4595            constants::INVALID_INDEX
4596        )
4597    }
4598
4599    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16>
4600    #[expect(unsafe_code)]
4601    fn GetActiveUniformBlockParameter(
4602        &self,
4603        cx: JSContext,
4604        program: &WebGLProgram,
4605        block_index: u32,
4606        pname: u32,
4607        mut retval: MutableHandleValue,
4608    ) {
4609        handle_potential_webgl_error!(
4610            self.base,
4611            self.base.validate_ownership(program),
4612            return retval.set(NullValue())
4613        );
4614        let values = handle_potential_webgl_error!(
4615            self.base,
4616            program.get_active_uniform_block_parameter(block_index, pname),
4617            return retval.set(NullValue())
4618        );
4619        match pname {
4620            constants::UNIFORM_BLOCK_BINDING |
4621            constants::UNIFORM_BLOCK_DATA_SIZE |
4622            constants::UNIFORM_BLOCK_ACTIVE_UNIFORMS => {
4623                assert!(values.len() == 1);
4624                retval.set(UInt32Value(values[0] as u32))
4625            },
4626            constants::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES => unsafe {
4627                let values = values.iter().map(|&v| v as u32).collect::<Vec<_>>();
4628                rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>());
4629                Uint32Array::create(*cx, CreateWith::Slice(&values), result.handle_mut()).unwrap();
4630                retval.set(ObjectValue(result.get()))
4631            },
4632            constants::UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER |
4633            constants::UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER => {
4634                assert!(values.len() == 1);
4635                retval.set(BooleanValue(values[0] != 0))
4636            },
4637            _ => unreachable!(),
4638        }
4639    }
4640
4641    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16>
4642    fn GetActiveUniformBlockName(
4643        &self,
4644        program: &WebGLProgram,
4645        block_index: u32,
4646    ) -> Option<DOMString> {
4647        handle_potential_webgl_error!(
4648            self.base,
4649            self.base.validate_ownership(program),
4650            return None
4651        );
4652        let name = handle_potential_webgl_error!(
4653            self.base,
4654            program.get_active_uniform_block_name(block_index),
4655            return None
4656        );
4657        Some(DOMString::from(name))
4658    }
4659
4660    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16>
4661    fn UniformBlockBinding(&self, program: &WebGLProgram, block_index: u32, block_binding: u32) {
4662        handle_potential_webgl_error!(self.base, self.base.validate_ownership(program), return);
4663
4664        if block_binding >= self.base.limits().max_uniform_buffer_bindings {
4665            return self.base.webgl_error(InvalidValue);
4666        }
4667
4668        handle_potential_webgl_error!(
4669            self.base,
4670            program.bind_uniform_block(block_index, block_binding)
4671        )
4672    }
4673
4674    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.11>
4675    fn ClearBufferfv(
4676        &self,
4677        buffer: u32,
4678        draw_buffer: i32,
4679        values: Float32ArrayOrUnrestrictedFloatSequence,
4680        src_offset: u32,
4681    ) {
4682        let array = match values {
4683            Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
4684            Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
4685        };
4686        self.clear_buffer::<f32>(
4687            buffer,
4688            draw_buffer,
4689            &[constants::COLOR, constants::DEPTH],
4690            src_offset,
4691            array,
4692            WebGLCommand::ClearBufferfv,
4693        )
4694    }
4695
4696    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.11>
4697    fn ClearBufferiv(
4698        &self,
4699        buffer: u32,
4700        draw_buffer: i32,
4701        values: Int32ArrayOrLongSequence,
4702        src_offset: u32,
4703    ) {
4704        let array = match values {
4705            Int32ArrayOrLongSequence::Int32Array(v) => v.to_vec(),
4706            Int32ArrayOrLongSequence::LongSequence(v) => v,
4707        };
4708        self.clear_buffer::<i32>(
4709            buffer,
4710            draw_buffer,
4711            &[constants::COLOR, constants::STENCIL],
4712            src_offset,
4713            array,
4714            WebGLCommand::ClearBufferiv,
4715        )
4716    }
4717
4718    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.11>
4719    fn ClearBufferuiv(
4720        &self,
4721        buffer: u32,
4722        draw_buffer: i32,
4723        values: Uint32ArrayOrUnsignedLongSequence,
4724        src_offset: u32,
4725    ) {
4726        let array = match values {
4727            Uint32ArrayOrUnsignedLongSequence::Uint32Array(v) => v.to_vec(),
4728            Uint32ArrayOrUnsignedLongSequence::UnsignedLongSequence(v) => v,
4729        };
4730        self.clear_buffer::<u32>(
4731            buffer,
4732            draw_buffer,
4733            &[constants::COLOR],
4734            src_offset,
4735            array,
4736            WebGLCommand::ClearBufferuiv,
4737        )
4738    }
4739
4740    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.11>
4741    fn ClearBufferfi(&self, buffer: u32, draw_buffer: i32, depth: f32, stencil: i32) {
4742        if buffer != constants::DEPTH_STENCIL {
4743            return self.base.webgl_error(InvalidEnum);
4744        }
4745
4746        handle_potential_webgl_error!(
4747            self.base,
4748            self.clearbuffer_array_size(buffer, draw_buffer),
4749            return
4750        );
4751
4752        self.base.send_command(WebGLCommand::ClearBufferfi(
4753            buffer,
4754            draw_buffer,
4755            depth,
4756            stencil,
4757        ));
4758    }
4759
4760    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4>
4761    fn InvalidateFramebuffer(&self, target: u32, attachments: Vec<u32>) {
4762        if !self.valid_fb_attachment_values(target, &attachments) {
4763            return;
4764        }
4765
4766        self.base
4767            .send_command(WebGLCommand::InvalidateFramebuffer(target, attachments))
4768    }
4769
4770    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4>
4771    fn InvalidateSubFramebuffer(
4772        &self,
4773        target: u32,
4774        attachments: Vec<u32>,
4775        x: i32,
4776        y: i32,
4777        width: i32,
4778        height: i32,
4779    ) {
4780        if !self.valid_fb_attachment_values(target, &attachments) {
4781            return;
4782        }
4783
4784        if width < 0 || height < 0 {
4785            return self.base.webgl_error(InvalidValue);
4786        }
4787
4788        self.base
4789            .send_command(WebGLCommand::InvalidateSubFramebuffer(
4790                target,
4791                attachments,
4792                x,
4793                y,
4794                width,
4795                height,
4796            ))
4797    }
4798
4799    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4>
4800    fn FramebufferTextureLayer(
4801        &self,
4802        target: u32,
4803        attachment: u32,
4804        texture: Option<&WebGLTexture>,
4805        level: i32,
4806        layer: i32,
4807    ) {
4808        if let Some(tex) = texture {
4809            handle_potential_webgl_error!(self.base, self.base.validate_ownership(tex), return);
4810        }
4811
4812        let fb_slot = match target {
4813            constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => {
4814                self.base.get_draw_framebuffer_slot()
4815            },
4816            constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(),
4817            _ => return self.base.webgl_error(InvalidEnum),
4818        };
4819
4820        match fb_slot.get() {
4821            Some(fb) => handle_potential_webgl_error!(
4822                self.base,
4823                fb.texture_layer(attachment, texture, level, layer)
4824            ),
4825            None => self.base.webgl_error(InvalidOperation),
4826        }
4827    }
4828
4829    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.5>
4830    #[expect(unsafe_code)]
4831    fn GetInternalformatParameter(
4832        &self,
4833        cx: JSContext,
4834        target: u32,
4835        internal_format: u32,
4836        pname: u32,
4837        mut retval: MutableHandleValue,
4838    ) {
4839        if target != constants::RENDERBUFFER {
4840            self.base.webgl_error(InvalidEnum);
4841            return retval.set(NullValue());
4842        }
4843
4844        match handle_potential_webgl_error!(
4845            self.base,
4846            InternalFormatParameter::from_u32(pname),
4847            return retval.set(NullValue())
4848        ) {
4849            InternalFormatParameter::IntVec(param) => unsafe {
4850                let (sender, receiver) = webgl_channel().unwrap();
4851                self.base
4852                    .send_command(WebGLCommand::GetInternalFormatIntVec(
4853                        target,
4854                        internal_format,
4855                        param,
4856                        sender,
4857                    ));
4858
4859                rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
4860                Int32Array::create(
4861                    *cx,
4862                    CreateWith::Slice(&receiver.recv().unwrap()),
4863                    rval.handle_mut(),
4864                )
4865                .unwrap();
4866                retval.set(ObjectValue(rval.get()))
4867            },
4868        }
4869    }
4870
4871    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.5>
4872    fn RenderbufferStorageMultisample(
4873        &self,
4874        target: u32,
4875        samples: i32,
4876        internal_format: u32,
4877        width: i32,
4878        height: i32,
4879    ) {
4880        self.base
4881            .renderbuffer_storage(target, samples, internal_format, width, height)
4882    }
4883
4884    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4>
4885    fn ReadBuffer(&self, src: u32) {
4886        match src {
4887            constants::BACK | constants::NONE => {},
4888            _ if self.base.valid_color_attachment_enum(src) => {},
4889            _ => return self.base.webgl_error(InvalidEnum),
4890        }
4891
4892        if let Some(fb) = self.base.get_read_framebuffer_slot().get() {
4893            handle_potential_webgl_error!(self.base, fb.set_read_buffer(src))
4894        } else {
4895            match src {
4896                constants::NONE | constants::BACK => {},
4897                _ => return self.base.webgl_error(InvalidOperation),
4898            }
4899
4900            self.default_fb_readbuffer.set(src);
4901            self.base.send_command(WebGLCommand::ReadBuffer(src));
4902        }
4903    }
4904
4905    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.11>
4906    fn DrawBuffers(&self, buffers: Vec<u32>) {
4907        if let Some(fb) = self.base.get_draw_framebuffer_slot().get() {
4908            handle_potential_webgl_error!(self.base, fb.set_draw_buffers(buffers))
4909        } else {
4910            if buffers.len() != 1 {
4911                return self.base.webgl_error(InvalidOperation);
4912            }
4913
4914            match buffers[0] {
4915                constants::NONE | constants::BACK => {},
4916                _ => return self.base.webgl_error(InvalidOperation),
4917            }
4918
4919            self.default_fb_drawbuffer.set(buffers[0]);
4920            self.base.send_command(WebGLCommand::DrawBuffers(buffers));
4921        }
4922    }
4923
4924    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.6>
4925    fn TexStorage2D(
4926        &self,
4927        target: u32,
4928        levels: i32,
4929        internal_format: u32,
4930        width: i32,
4931        height: i32,
4932    ) {
4933        self.tex_storage(2, target, levels, internal_format, width, height, 1)
4934    }
4935
4936    /// <https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.6>
4937    fn TexStorage3D(
4938        &self,
4939        target: u32,
4940        levels: i32,
4941        internal_format: u32,
4942        width: i32,
4943        height: i32,
4944        depth: i32,
4945    ) {
4946        self.tex_storage(3, target, levels, internal_format, width, height, depth)
4947    }
4948
4949    /// <https://immersive-web.github.io/webxr/#dom-webglrenderingcontextbase-makexrcompatible>
4950    #[cfg(feature = "webxr")]
4951    fn MakeXRCompatible(&self, can_gc: CanGc) -> Rc<Promise> {
4952        // XXXManishearth Fill in with compatibility checks when rust-webxr supports this
4953        let p = Promise::new(&self.global(), can_gc);
4954        p.resolve_native(&(), can_gc);
4955        p
4956    }
4957}
4958
4959impl WebGL2RenderingContextHelpers for WebGL2RenderingContext {
4960    fn is_webgl2_enabled(cx: JSContext, global: HandleObject) -> bool {
4961        Self::is_webgl2_enabled(cx, global)
4962    }
4963}