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