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