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