Skip to main content

script/dom/webgl/
webgl2renderingcontext.rs

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