Skip to main content

script/dom/webgl/
webgl2renderingcontext.rs

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