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