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