Skip to main content

script/dom/webgl/
webgl2renderingcontext.rs

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