script/dom/webgl/
webglrenderingcontext.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;
7use std::ptr::{self, NonNull};
8#[cfg(feature = "webxr")]
9use std::rc::Rc;
10
11#[cfg(feature = "webgl_backtrace")]
12use backtrace::Backtrace;
13use base::Epoch;
14use bitflags::bitflags;
15use canvas_traits::webgl::WebGLError::*;
16use canvas_traits::webgl::{
17    AlphaTreatment, GLContextAttributes, GLLimits, GlType, Parameter, SizedDataType, TexDataType,
18    TexFormat, TexParameter, WebGLChan, WebGLCommand, WebGLCommandBacktrace, WebGLContextId,
19    WebGLError, WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender, WebGLProgramId,
20    WebGLResult, WebGLSLVersion, WebGLSendResult, WebGLSender, WebGLVersion, YAxisTreatment,
21    webgl_channel,
22};
23use dom_struct::dom_struct;
24use euclid::default::{Point2D, Rect, Size2D};
25use ipc_channel::ipc::{self, IpcSharedMemory};
26use js::jsapi::{JSContext, JSObject, Type};
27use js::jsval::{BooleanValue, DoubleValue, Int32Value, NullValue, ObjectValue, UInt32Value};
28use js::rust::{CustomAutoRooterGuard, MutableHandleValue};
29use js::typedarray::{
30    ArrayBufferView, CreateWith, Float32, Float32Array, Int32, Int32Array, TypedArray,
31    TypedArrayElementCreator, Uint32Array,
32};
33use pixels::{self, Alpha, PixelFormat, Snapshot, SnapshotPixelFormat};
34use script_bindings::conversions::SafeToJSValConvertible;
35use serde::{Deserialize, Serialize};
36use servo_config::pref;
37use webrender_api::ImageKey;
38
39use crate::canvas_context::{CanvasContext, HTMLCanvasElementOrOffscreenCanvas};
40use crate::dom::bindings::cell::{DomRefCell, Ref, RefMut};
41use crate::dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants;
42use crate::dom::bindings::codegen::Bindings::EXTBlendMinmaxBinding::EXTBlendMinmaxConstants;
43use crate::dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::OESVertexArrayObjectConstants;
44use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants;
45use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{
46    TexImageSource, WebGLContextAttributes, WebGLRenderingContextConstants as constants,
47    WebGLRenderingContextMethods,
48};
49use crate::dom::bindings::codegen::UnionTypes::{
50    ArrayBufferViewOrArrayBuffer, Float32ArrayOrUnrestrictedFloatSequence,
51    HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas,
52    Int32ArrayOrLongSequence,
53};
54use crate::dom::bindings::conversions::DerivedFrom;
55use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
56use crate::dom::bindings::inheritance::Castable;
57use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
58use crate::dom::bindings::root::{DomOnceCell, DomRoot, MutNullableDom};
59use crate::dom::bindings::str::DOMString;
60use crate::dom::event::{Event, EventBubbles, EventCancelable};
61#[cfg(feature = "webgl_backtrace")]
62use crate::dom::globalscope::GlobalScope;
63use crate::dom::node::NodeTraits;
64#[cfg(feature = "webxr")]
65use crate::dom::promise::Promise;
66use crate::dom::webgl::extensions::WebGLExtensions;
67use crate::dom::webgl::validations::WebGLValidator;
68use crate::dom::webgl::validations::tex_image_2d::{
69    CommonCompressedTexImage2DValidatorResult, CommonTexImage2DValidator,
70    CommonTexImage2DValidatorResult, CompressedTexImage2DValidator,
71    CompressedTexSubImage2DValidator, TexImage2DValidator, TexImage2DValidatorResult,
72};
73use crate::dom::webgl::validations::types::TexImageTarget;
74use crate::dom::webgl::vertexarrayobject::VertexAttribData;
75use crate::dom::webgl::webglactiveinfo::WebGLActiveInfo;
76use crate::dom::webgl::webglbuffer::WebGLBuffer;
77use crate::dom::webgl::webglcontextevent::WebGLContextEvent;
78use crate::dom::webgl::webglframebuffer::{
79    CompleteForRendering, WebGLFramebuffer, WebGLFramebufferAttachmentRoot,
80};
81use crate::dom::webgl::webglobject::WebGLObject;
82use crate::dom::webgl::webglprogram::WebGLProgram;
83use crate::dom::webgl::webglrenderbuffer::WebGLRenderbuffer;
84use crate::dom::webgl::webglshader::WebGLShader;
85use crate::dom::webgl::webglshaderprecisionformat::WebGLShaderPrecisionFormat;
86use crate::dom::webgl::webgltexture::{TexParameterValue, WebGLTexture};
87use crate::dom::webgl::webgluniformlocation::WebGLUniformLocation;
88use crate::dom::webgl::webglvertexarrayobject::WebGLVertexArrayObject;
89use crate::dom::webgl::webglvertexarrayobjectoes::WebGLVertexArrayObjectOES;
90use crate::dom::window::Window;
91use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
92
93// From the GLES 2.0.25 spec, page 85:
94//
95//     "If a texture that is currently bound to one of the targets
96//      TEXTURE_2D, or TEXTURE_CUBE_MAP is deleted, it is as though
97//      BindTexture had been executed with the same target and texture
98//      zero."
99//
100// and similar text occurs for other object types.
101macro_rules! handle_object_deletion {
102    ($self_:expr, $binding:expr, $object:ident, $unbind_command:expr) => {
103        if let Some(bound_object) = $binding.get() {
104            if bound_object.id() == $object.id() {
105                $binding.set(None);
106                if let Some(command) = $unbind_command {
107                    $self_.send_command(command);
108                }
109            }
110        }
111    };
112}
113
114fn has_invalid_blend_constants(arg1: u32, arg2: u32) -> bool {
115    match (arg1, arg2) {
116        (constants::CONSTANT_COLOR, constants::CONSTANT_ALPHA) => true,
117        (constants::ONE_MINUS_CONSTANT_COLOR, constants::ONE_MINUS_CONSTANT_ALPHA) => true,
118        (constants::ONE_MINUS_CONSTANT_COLOR, constants::CONSTANT_ALPHA) => true,
119        (constants::CONSTANT_COLOR, constants::ONE_MINUS_CONSTANT_ALPHA) => true,
120        (_, _) => false,
121    }
122}
123
124pub(crate) fn uniform_get<T, F>(triple: (&WebGLRenderingContext, WebGLProgramId, i32), f: F) -> T
125where
126    F: FnOnce(WebGLProgramId, i32, WebGLSender<T>) -> WebGLCommand,
127    T: for<'de> Deserialize<'de> + Serialize,
128{
129    let (sender, receiver) = webgl_channel().unwrap();
130    triple.0.send_command(f(triple.1, triple.2, sender));
131    receiver.recv().unwrap()
132}
133
134#[allow(unsafe_code)]
135pub(crate) unsafe fn uniform_typed<T>(
136    cx: *mut JSContext,
137    value: &[T::Element],
138    mut retval: MutableHandleValue,
139) where
140    T: TypedArrayElementCreator,
141{
142    rooted!(in(cx) let mut rval = ptr::null_mut::<JSObject>());
143    <TypedArray<T, *mut JSObject>>::create(cx, CreateWith::Slice(value), rval.handle_mut())
144        .unwrap();
145    retval.set(ObjectValue(rval.get()));
146}
147
148/// Set of bitflags for texture unpacking (texImage2d, etc...)
149#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
150pub(crate) struct TextureUnpacking(u8);
151
152bitflags! {
153    impl TextureUnpacking: u8 {
154        const FLIP_Y_AXIS = 0x01;
155        const PREMULTIPLY_ALPHA = 0x02;
156        const CONVERT_COLORSPACE = 0x04;
157    }
158}
159
160#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf)]
161pub(crate) enum VertexAttrib {
162    Float(f32, f32, f32, f32),
163    Int(i32, i32, i32, i32),
164    Uint(u32, u32, u32, u32),
165}
166
167#[derive(Clone, Copy, Debug)]
168pub(crate) enum Operation {
169    Fallible,
170    Infallible,
171}
172
173#[dom_struct]
174pub(crate) struct WebGLRenderingContext {
175    reflector_: Reflector,
176    #[no_trace]
177    webgl_sender: WebGLMsgSender,
178    #[no_trace]
179    webgl_version: WebGLVersion,
180    #[no_trace]
181    glsl_version: WebGLSLVersion,
182    #[ignore_malloc_size_of = "Defined in surfman"]
183    #[no_trace]
184    limits: GLLimits,
185    canvas: HTMLCanvasElementOrOffscreenCanvas,
186    #[ignore_malloc_size_of = "Defined in canvas_traits"]
187    #[no_trace]
188    last_error: Cell<Option<WebGLError>>,
189    texture_packing_alignment: Cell<u8>,
190    texture_unpacking_settings: Cell<TextureUnpacking>,
191    // TODO(nox): Should be Cell<u8>.
192    texture_unpacking_alignment: Cell<u32>,
193    bound_draw_framebuffer: MutNullableDom<WebGLFramebuffer>,
194    // TODO(mmatyas): This was introduced in WebGL2, but listed here because it's used by
195    // Textures and Renderbuffers, but such WebGLObjects have access only to the GL1 context.
196    bound_read_framebuffer: MutNullableDom<WebGLFramebuffer>,
197    bound_renderbuffer: MutNullableDom<WebGLRenderbuffer>,
198    bound_buffer_array: MutNullableDom<WebGLBuffer>,
199    current_program: MutNullableDom<WebGLProgram>,
200    current_vertex_attribs: DomRefCell<Box<[VertexAttrib]>>,
201    #[ignore_malloc_size_of = "Because it's small"]
202    current_scissor: Cell<(i32, i32, u32, u32)>,
203    #[ignore_malloc_size_of = "Because it's small"]
204    current_clear_color: Cell<(f32, f32, f32, f32)>,
205    #[no_trace]
206    size: Cell<Size2D<u32>>,
207    extension_manager: WebGLExtensions,
208    capabilities: Capabilities,
209    default_vao: DomOnceCell<WebGLVertexArrayObjectOES>,
210    current_vao: MutNullableDom<WebGLVertexArrayObjectOES>,
211    default_vao_webgl2: DomOnceCell<WebGLVertexArrayObject>,
212    current_vao_webgl2: MutNullableDom<WebGLVertexArrayObject>,
213    textures: Textures,
214    #[no_trace]
215    api_type: GlType,
216}
217
218impl WebGLRenderingContext {
219    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
220    pub(crate) fn new_inherited(
221        window: &Window,
222        canvas: HTMLCanvasElementOrOffscreenCanvas,
223        webgl_version: WebGLVersion,
224        size: Size2D<u32>,
225        attrs: GLContextAttributes,
226    ) -> Result<WebGLRenderingContext, String> {
227        if pref!(webgl_testing_context_creation_error) {
228            return Err("WebGL context creation error forced by pref `webgl.testing.context_creation_error`".into());
229        }
230
231        let webgl_chan = match window.webgl_chan() {
232            Some(chan) => chan,
233            None => return Err("WebGL initialization failed early on".into()),
234        };
235
236        let (sender, receiver) = webgl_channel().unwrap();
237        webgl_chan
238            .send(WebGLMsg::CreateContext(webgl_version, size, attrs, sender))
239            .unwrap();
240        let result = receiver.recv().unwrap();
241
242        result.map(|ctx_data| {
243            let max_combined_texture_image_units = ctx_data.limits.max_combined_texture_image_units;
244            let max_vertex_attribs = ctx_data.limits.max_vertex_attribs as usize;
245            Self {
246                reflector_: Reflector::new(),
247                webgl_sender: ctx_data.sender,
248                webgl_version,
249                glsl_version: ctx_data.glsl_version,
250                limits: ctx_data.limits,
251                canvas,
252                last_error: Cell::new(None),
253                texture_packing_alignment: Cell::new(4),
254                texture_unpacking_settings: Cell::new(TextureUnpacking::CONVERT_COLORSPACE),
255                texture_unpacking_alignment: Cell::new(4),
256                bound_draw_framebuffer: MutNullableDom::new(None),
257                bound_read_framebuffer: MutNullableDom::new(None),
258                bound_buffer_array: MutNullableDom::new(None),
259                bound_renderbuffer: MutNullableDom::new(None),
260                current_program: MutNullableDom::new(None),
261                current_vertex_attribs: DomRefCell::new(
262                    vec![VertexAttrib::Float(0f32, 0f32, 0f32, 1f32); max_vertex_attribs].into(),
263                ),
264                current_scissor: Cell::new((0, 0, size.width, size.height)),
265                // FIXME(#21718) The backend is allowed to choose a size smaller than
266                // what was requested
267                size: Cell::new(size),
268                current_clear_color: Cell::new((0.0, 0.0, 0.0, 0.0)),
269                extension_manager: WebGLExtensions::new(
270                    webgl_version,
271                    ctx_data.api_type,
272                    ctx_data.glsl_version,
273                ),
274                capabilities: Default::default(),
275                default_vao: Default::default(),
276                current_vao: Default::default(),
277                default_vao_webgl2: Default::default(),
278                current_vao_webgl2: Default::default(),
279                textures: Textures::new(max_combined_texture_image_units),
280                api_type: ctx_data.api_type,
281            }
282        })
283    }
284
285    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
286    pub(crate) fn new(
287        window: &Window,
288        canvas: &RootedHTMLCanvasElementOrOffscreenCanvas,
289        webgl_version: WebGLVersion,
290        size: Size2D<u32>,
291        attrs: GLContextAttributes,
292        can_gc: CanGc,
293    ) -> Option<DomRoot<WebGLRenderingContext>> {
294        match WebGLRenderingContext::new_inherited(
295            window,
296            HTMLCanvasElementOrOffscreenCanvas::from(canvas),
297            webgl_version,
298            size,
299            attrs,
300        ) {
301            Ok(ctx) => Some(reflect_dom_object(Box::new(ctx), window, can_gc)),
302            Err(msg) => {
303                error!("Couldn't create WebGLRenderingContext: {}", msg);
304                let event = WebGLContextEvent::new(
305                    window,
306                    atom!("webglcontextcreationerror"),
307                    EventBubbles::DoesNotBubble,
308                    EventCancelable::Cancelable,
309                    DOMString::from(msg),
310                    can_gc,
311                );
312                match canvas {
313                    RootedHTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => {
314                        event.upcast::<Event>().fire(canvas.upcast(), can_gc);
315                    },
316                    RootedHTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => {
317                        event.upcast::<Event>().fire(canvas.upcast(), can_gc);
318                    },
319                }
320                None
321            },
322        }
323    }
324
325    pub(crate) fn set_image_key(&self, image_key: ImageKey) {
326        self.webgl_sender.set_image_key(image_key);
327    }
328
329    pub(crate) fn update_rendering(&self, canvas_epoch: Epoch) -> bool {
330        if !self.onscreen() {
331            return false;
332        }
333
334        let global = self.global();
335        let Some(window) = global.downcast::<Window>() else {
336            return false;
337        };
338
339        window
340            .webgl_chan()
341            .expect("Where's the WebGL channel?")
342            .send(WebGLMsg::SwapBuffers(
343                vec![self.context_id()],
344                Some(canvas_epoch),
345                0, /* time */
346            ))
347            .is_ok()
348    }
349
350    pub(crate) fn webgl_version(&self) -> WebGLVersion {
351        self.webgl_version
352    }
353
354    pub(crate) fn limits(&self) -> &GLLimits {
355        &self.limits
356    }
357
358    pub(crate) fn texture_unpacking_alignment(&self) -> u32 {
359        self.texture_unpacking_alignment.get()
360    }
361
362    pub(crate) fn bound_draw_framebuffer(&self) -> Option<DomRoot<WebGLFramebuffer>> {
363        self.bound_draw_framebuffer.get()
364    }
365
366    pub(crate) fn current_vao(&self) -> DomRoot<WebGLVertexArrayObjectOES> {
367        self.current_vao.or_init(|| {
368            DomRoot::from_ref(
369                self.default_vao
370                    .init_once(|| WebGLVertexArrayObjectOES::new(self, None, CanGc::note())),
371            )
372        })
373    }
374
375    pub(crate) fn current_vao_webgl2(&self) -> DomRoot<WebGLVertexArrayObject> {
376        self.current_vao_webgl2.or_init(|| {
377            DomRoot::from_ref(
378                self.default_vao_webgl2
379                    .init_once(|| WebGLVertexArrayObject::new(self, None, CanGc::note())),
380            )
381        })
382    }
383
384    pub(crate) fn current_vertex_attribs(&self) -> RefMut<'_, Box<[VertexAttrib]>> {
385        self.current_vertex_attribs.borrow_mut()
386    }
387
388    #[inline]
389    pub(crate) fn send_command(&self, command: WebGLCommand) {
390        self.webgl_sender
391            .send(command, capture_webgl_backtrace())
392            .unwrap();
393    }
394
395    pub(crate) fn send_command_ignored(&self, command: WebGLCommand) {
396        let _ = self.webgl_sender.send(command, capture_webgl_backtrace());
397    }
398
399    pub(crate) fn webgl_error(&self, err: WebGLError) {
400        // TODO(emilio): Add useful debug messages to this
401        warn!(
402            "WebGL error: {:?}, previous error was {:?}",
403            err,
404            self.last_error.get()
405        );
406
407        // If an error has been detected no further errors must be
408        // recorded until `getError` has been called
409        if self.last_error.get().is_none() {
410            self.last_error.set(Some(err));
411        }
412    }
413
414    // Helper function for validating framebuffer completeness in
415    // calls touching the framebuffer.  From the GLES 2.0.25 spec,
416    // page 119:
417    //
418    //    "Effects of Framebuffer Completeness on Framebuffer
419    //     Operations
420    //
421    //     If the currently bound framebuffer is not framebuffer
422    //     complete, then it is an error to attempt to use the
423    //     framebuffer for writing or reading. This means that
424    //     rendering commands such as DrawArrays and DrawElements, as
425    //     well as commands that read the framebuffer such as
426    //     ReadPixels and CopyTexSubImage, will generate the error
427    //     INVALID_FRAMEBUFFER_OPERATION if called while the
428    //     framebuffer is not framebuffer complete."
429    //
430    // The WebGL spec mentions a couple more operations that trigger
431    // this: clear() and getParameter(IMPLEMENTATION_COLOR_READ_*).
432    pub(crate) fn validate_framebuffer(&self) -> WebGLResult<()> {
433        match self.bound_draw_framebuffer.get() {
434            Some(fb) => match fb.check_status_for_rendering() {
435                CompleteForRendering::Complete => Ok(()),
436                CompleteForRendering::Incomplete => Err(InvalidFramebufferOperation),
437                CompleteForRendering::MissingColorAttachment => Err(InvalidOperation),
438            },
439            None => Ok(()),
440        }
441    }
442
443    pub(crate) fn validate_ownership<T>(&self, object: &T) -> WebGLResult<()>
444    where
445        T: DerivedFrom<WebGLObject>,
446    {
447        if self != object.upcast().context() {
448            return Err(InvalidOperation);
449        }
450        Ok(())
451    }
452
453    pub(crate) fn with_location<F>(&self, location: Option<&WebGLUniformLocation>, f: F)
454    where
455        F: FnOnce(&WebGLUniformLocation) -> WebGLResult<()>,
456    {
457        let location = match location {
458            Some(loc) => loc,
459            None => return,
460        };
461        match self.current_program.get() {
462            Some(ref program)
463                if program.id() == location.program_id() &&
464                    program.link_generation() == location.link_generation() => {},
465            _ => return self.webgl_error(InvalidOperation),
466        }
467        handle_potential_webgl_error!(self, f(location));
468    }
469
470    pub(crate) fn textures(&self) -> &Textures {
471        &self.textures
472    }
473
474    fn tex_parameter(&self, target: u32, param: u32, value: TexParameterValue) {
475        let texture_slot = handle_potential_webgl_error!(
476            self,
477            self.textures
478                .active_texture_slot(target, self.webgl_version()),
479            return
480        );
481        let texture =
482            handle_potential_webgl_error!(self, texture_slot.get().ok_or(InvalidOperation), return);
483
484        if !self
485            .extension_manager
486            .is_get_tex_parameter_name_enabled(param)
487        {
488            return self.webgl_error(InvalidEnum);
489        }
490
491        handle_potential_webgl_error!(self, texture.tex_parameter(param, value), return);
492
493        // Validate non filterable TEXTURE_2D data_types
494        if target != constants::TEXTURE_2D {
495            return;
496        }
497
498        let target = TexImageTarget::Texture2D;
499        if let Some(info) = texture.image_info_for_target(&target, 0) {
500            self.validate_filterable_texture(
501                &texture,
502                target,
503                0,
504                info.internal_format(),
505                Size2D::new(info.width(), info.height()),
506                info.data_type().unwrap_or(TexDataType::UnsignedByte),
507            );
508        }
509    }
510
511    fn vertex_attrib(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) {
512        if indx >= self.limits.max_vertex_attribs {
513            return self.webgl_error(InvalidValue);
514        }
515
516        match self.webgl_version() {
517            WebGLVersion::WebGL1 => self
518                .current_vao()
519                .set_vertex_attrib_type(indx, constants::FLOAT),
520            WebGLVersion::WebGL2 => self
521                .current_vao_webgl2()
522                .set_vertex_attrib_type(indx, constants::FLOAT),
523        };
524        self.current_vertex_attribs.borrow_mut()[indx as usize] = VertexAttrib::Float(x, y, z, w);
525
526        self.send_command(WebGLCommand::VertexAttrib(indx, x, y, z, w));
527    }
528
529    pub(crate) fn get_current_framebuffer_size(&self) -> Option<(i32, i32)> {
530        match self.bound_draw_framebuffer.get() {
531            Some(fb) => fb.size(),
532
533            // The window system framebuffer is bound
534            None => Some((self.DrawingBufferWidth(), self.DrawingBufferHeight())),
535        }
536    }
537
538    pub(crate) fn get_texture_packing_alignment(&self) -> u8 {
539        self.texture_packing_alignment.get()
540    }
541
542    pub(crate) fn get_current_unpack_state(
543        &self,
544        premultiplied: Alpha,
545    ) -> (Option<AlphaTreatment>, YAxisTreatment) {
546        let settings = self.texture_unpacking_settings.get();
547        let dest_premultiplied = settings.contains(TextureUnpacking::PREMULTIPLY_ALPHA);
548
549        let alpha_treatment = match (premultiplied, dest_premultiplied) {
550            (Alpha::Premultiplied, false) => Some(AlphaTreatment::Unmultiply),
551            (Alpha::NotPremultiplied, true) => Some(AlphaTreatment::Premultiply),
552            _ => None,
553        };
554
555        let y_axis_treatment = if settings.contains(TextureUnpacking::FLIP_Y_AXIS) {
556            YAxisTreatment::Flipped
557        } else {
558            YAxisTreatment::AsIs
559        };
560
561        (alpha_treatment, y_axis_treatment)
562    }
563
564    // LINEAR filtering may be forbidden when using WebGL extensions.
565    /// <https://www.khronos.org/registry/webgl/extensions/OES_texture_float_linear/>
566    fn validate_filterable_texture(
567        &self,
568        texture: &WebGLTexture,
569        target: TexImageTarget,
570        level: u32,
571        internal_format: TexFormat,
572        size: Size2D<u32>,
573        data_type: TexDataType,
574    ) -> bool {
575        if self
576            .extension_manager
577            .is_filterable(data_type.as_gl_constant()) ||
578            !texture.is_using_linear_filtering()
579        {
580            return true;
581        }
582
583        // Handle validation failed: LINEAR filtering not valid for this texture
584        // WebGL Conformance tests expect to fallback to [0, 0, 0, 255] RGBA UNSIGNED_BYTE
585        let data_type = TexDataType::UnsignedByte;
586        let expected_byte_length = size.area() * 4;
587        let mut pixels = vec![0u8; expected_byte_length as usize];
588        for rgba8 in pixels.chunks_mut(4) {
589            rgba8[3] = 255u8;
590        }
591
592        // TODO(nox): AFAICT here we construct a RGBA8 array and then we
593        // convert it to whatever actual format we need, we should probably
594        // construct the desired format from the start.
595        self.tex_image_2d(
596            texture,
597            target,
598            data_type,
599            internal_format,
600            internal_format.to_unsized(),
601            level,
602            0,
603            1,
604            size,
605            TexSource::Pixels(TexPixels::new(
606                IpcSharedMemory::from_bytes(&pixels),
607                size,
608                PixelFormat::RGBA8,
609                None,
610                YAxisTreatment::AsIs,
611            )),
612        );
613
614        false
615    }
616
617    fn validate_stencil_actions(&self, action: u32) -> bool {
618        matches!(
619            action,
620            0 | constants::KEEP |
621                constants::REPLACE |
622                constants::INCR |
623                constants::DECR |
624                constants::INVERT |
625                constants::INCR_WRAP |
626                constants::DECR_WRAP
627        )
628    }
629
630    pub(crate) fn get_image_pixels(&self, source: TexImageSource) -> Fallible<Option<TexPixels>> {
631        Ok(Some(match source {
632            TexImageSource::ImageBitmap(bitmap) => {
633                if !bitmap.origin_is_clean() {
634                    return Err(Error::Security);
635                }
636
637                let Some(snapshot) = bitmap.bitmap_data().clone() else {
638                    return Ok(None);
639                };
640
641                let snapshot = snapshot.to_shared();
642                let size = snapshot.size().cast();
643                let format = match snapshot.format() {
644                    SnapshotPixelFormat::RGBA => PixelFormat::RGBA8,
645                    SnapshotPixelFormat::BGRA => PixelFormat::BGRA8,
646                };
647
648                // If the TexImageSource is an ImageBitmap, the values of
649                // UNPACK_FLIP_Y, UNPACK_PREMULTIPLY_ALPHA, and
650                // UNPACK_COLORSPACE_CONVERSION are to be ignored.
651                // Set alpha and y_axis treatment parameters such that no
652                // conversions will be made.
653                // <https://registry.khronos.org/webgl/specs/latest/1.0/#6.10>
654                TexPixels::new(
655                    snapshot.shared_memory(),
656                    size,
657                    format,
658                    None,
659                    YAxisTreatment::AsIs,
660                )
661            },
662            TexImageSource::ImageData(image_data) => {
663                let (alpha_treatment, y_axis_treatment) =
664                    self.get_current_unpack_state(Alpha::NotPremultiplied);
665
666                TexPixels::new(
667                    image_data.to_shared_memory(),
668                    image_data.get_size(),
669                    PixelFormat::RGBA8,
670                    alpha_treatment,
671                    y_axis_treatment,
672                )
673            },
674            TexImageSource::HTMLImageElement(image) => {
675                let document = match self.canvas {
676                    HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(ref canvas) => {
677                        canvas.owner_document()
678                    },
679                    HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(ref _canvas) => {
680                        // TODO: Support retrieving image pixels here for OffscreenCanvas
681                        return Ok(None);
682                    },
683                };
684                if !image.same_origin(document.origin()) {
685                    return Err(Error::Security);
686                }
687
688                // Vector images are not currently supported here and there are
689                // some open questions in the specification about how to handle them:
690                // See https://github.com/KhronosGroup/WebGL/issues/1503
691                let Some(snapshot) = image.get_raster_image_data() else {
692                    return Ok(None);
693                };
694
695                let snapshot = snapshot.to_shared();
696                let size = snapshot.size().cast();
697                let format: PixelFormat = match snapshot.format() {
698                    SnapshotPixelFormat::RGBA => PixelFormat::RGBA8,
699                    SnapshotPixelFormat::BGRA => PixelFormat::BGRA8,
700                };
701
702                let (alpha_treatment, y_axis_treatment) =
703                    self.get_current_unpack_state(snapshot.alpha_mode().alpha());
704
705                TexPixels::new(
706                    snapshot.shared_memory(),
707                    size,
708                    format,
709                    alpha_treatment,
710                    y_axis_treatment,
711                )
712            },
713            // TODO(emilio): Getting canvas data is implemented in CanvasRenderingContext2D,
714            // but we need to refactor it moving it to `HTMLCanvasElement` and support
715            // WebGLContext (probably via GetPixels()).
716            TexImageSource::HTMLCanvasElement(canvas) => {
717                if !canvas.origin_is_clean() {
718                    return Err(Error::Security);
719                }
720
721                let Some(snapshot) = canvas.get_image_data() else {
722                    return Ok(None);
723                };
724
725                let snapshot = snapshot.to_shared();
726                let size = snapshot.size().cast();
727                let format = match snapshot.format() {
728                    SnapshotPixelFormat::RGBA => PixelFormat::RGBA8,
729                    SnapshotPixelFormat::BGRA => PixelFormat::BGRA8,
730                };
731
732                let (alpha_treatment, y_axis_treatment) =
733                    self.get_current_unpack_state(snapshot.alpha_mode().alpha());
734
735                TexPixels::new(
736                    snapshot.shared_memory(),
737                    size,
738                    format,
739                    alpha_treatment,
740                    y_axis_treatment,
741                )
742            },
743            TexImageSource::HTMLVideoElement(video) => {
744                if !video.origin_is_clean() {
745                    return Err(Error::Security);
746                }
747
748                let Some(snapshot) = video.get_current_frame_data() else {
749                    return Ok(None);
750                };
751
752                let snapshot = snapshot.to_shared();
753                let size = snapshot.size().cast();
754                let format: PixelFormat = match snapshot.format() {
755                    SnapshotPixelFormat::RGBA => PixelFormat::RGBA8,
756                    SnapshotPixelFormat::BGRA => PixelFormat::BGRA8,
757                };
758
759                let (alpha_treatment, y_axis_treatment) =
760                    self.get_current_unpack_state(snapshot.alpha_mode().alpha());
761
762                TexPixels::new(
763                    snapshot.shared_memory(),
764                    size,
765                    format,
766                    alpha_treatment,
767                    y_axis_treatment,
768                )
769            },
770        }))
771    }
772
773    // TODO(emilio): Move this logic to a validator.
774    pub(crate) fn validate_tex_image_2d_data(
775        &self,
776        width: u32,
777        height: u32,
778        format: TexFormat,
779        data_type: TexDataType,
780        unpacking_alignment: u32,
781        data: Option<&ArrayBufferView>,
782    ) -> Result<u32, ()> {
783        let element_size = data_type.element_size();
784        let components_per_element = data_type.components_per_element();
785        let components = format.components();
786
787        // If data is non-null, the type of pixels must match the type of the
788        // data to be read.
789        // If it is UNSIGNED_BYTE, a Uint8Array must be supplied;
790        // if it is UNSIGNED_SHORT_5_6_5, UNSIGNED_SHORT_4_4_4_4,
791        // or UNSIGNED_SHORT_5_5_5_1, a Uint16Array must be supplied.
792        // or FLOAT, a Float32Array must be supplied.
793        // If the types do not match, an INVALID_OPERATION error is generated.
794        let data_type_matches = data.as_ref().is_none_or(|buffer| {
795            Some(data_type.sized_data_type()) ==
796                array_buffer_type_to_sized_type(buffer.get_array_type()) &&
797                data_type.required_webgl_version() <= self.webgl_version()
798        });
799
800        if !data_type_matches {
801            self.webgl_error(InvalidOperation);
802            return Err(());
803        }
804
805        // NOTE: width and height are positive or zero due to validate()
806        if height == 0 {
807            Ok(0)
808        } else {
809            // We need to be careful here to not count unpack
810            // alignment at the end of the image, otherwise (for
811            // example) passing a single byte for uploading a 1x1
812            // GL_ALPHA/GL_UNSIGNED_BYTE texture would throw an error.
813            let cpp = element_size * components / components_per_element;
814            let stride = (width * cpp + unpacking_alignment - 1) & !(unpacking_alignment - 1);
815            Ok(stride * (height - 1) + width * cpp)
816        }
817    }
818
819    #[allow(clippy::too_many_arguments)]
820    pub(crate) fn tex_image_2d(
821        &self,
822        texture: &WebGLTexture,
823        target: TexImageTarget,
824        data_type: TexDataType,
825        internal_format: TexFormat,
826        format: TexFormat,
827        level: u32,
828        _border: u32,
829        unpacking_alignment: u32,
830        size: Size2D<u32>,
831        source: TexSource,
832    ) {
833        // TexImage2D depth is always equal to 1.
834        handle_potential_webgl_error!(
835            self,
836            texture.initialize(
837                target,
838                size.width,
839                size.height,
840                1,
841                format,
842                level,
843                Some(data_type)
844            )
845        );
846
847        let internal_format = self
848            .extension_manager
849            .get_effective_tex_internal_format(internal_format, data_type.as_gl_constant());
850
851        let effective_data_type = self
852            .extension_manager
853            .effective_type(data_type.as_gl_constant());
854
855        match source {
856            TexSource::Pixels(pixels) => {
857                // TODO(emilio): convert colorspace if requested.
858                self.send_command(WebGLCommand::TexImage2D {
859                    target: target.as_gl_constant(),
860                    level,
861                    internal_format,
862                    size,
863                    format,
864                    data_type,
865                    effective_data_type,
866                    unpacking_alignment,
867                    alpha_treatment: pixels.alpha_treatment,
868                    y_axis_treatment: pixels.y_axis_treatment,
869                    pixel_format: pixels.pixel_format,
870                    data: pixels.data.into(),
871                });
872            },
873            TexSource::BufferOffset(offset) => {
874                self.send_command(WebGLCommand::TexImage2DPBO {
875                    target: target.as_gl_constant(),
876                    level,
877                    internal_format,
878                    size,
879                    format,
880                    effective_data_type,
881                    unpacking_alignment,
882                    offset,
883                });
884            },
885        }
886
887        if let Some(fb) = self.bound_draw_framebuffer.get() {
888            fb.invalidate_texture(texture);
889        }
890    }
891
892    #[allow(clippy::too_many_arguments)]
893    fn tex_sub_image_2d(
894        &self,
895        texture: DomRoot<WebGLTexture>,
896        target: TexImageTarget,
897        level: u32,
898        xoffset: i32,
899        yoffset: i32,
900        format: TexFormat,
901        data_type: TexDataType,
902        unpacking_alignment: u32,
903        pixels: TexPixels,
904    ) {
905        // We have already validated level
906        let image_info = match texture.image_info_for_target(&target, level) {
907            Some(info) => info,
908            None => return self.webgl_error(InvalidOperation),
909        };
910
911        // GL_INVALID_VALUE is generated if:
912        //   - xoffset or yoffset is less than 0
913        //   - x offset plus the width is greater than the texture width
914        //   - y offset plus the height is greater than the texture height
915        if xoffset < 0 ||
916            (xoffset as u32 + pixels.size().width) > image_info.width() ||
917            yoffset < 0 ||
918            (yoffset as u32 + pixels.size().height) > image_info.height()
919        {
920            return self.webgl_error(InvalidValue);
921        }
922
923        // The unsized format must be compatible with the sized internal format
924        debug_assert!(!format.is_sized());
925        if format != image_info.internal_format().to_unsized() {
926            return self.webgl_error(InvalidOperation);
927        }
928
929        // See https://www.khronos.org/registry/webgl/specs/latest/2.0/#4.1.6
930        if self.webgl_version() == WebGLVersion::WebGL1 &&
931            data_type != image_info.data_type().unwrap()
932        {
933            return self.webgl_error(InvalidOperation);
934        }
935
936        let effective_data_type = self
937            .extension_manager
938            .effective_type(data_type.as_gl_constant());
939
940        // TODO(emilio): convert colorspace if requested.
941        self.send_command(WebGLCommand::TexSubImage2D {
942            target: target.as_gl_constant(),
943            level,
944            xoffset,
945            yoffset,
946            size: pixels.size(),
947            format,
948            data_type,
949            effective_data_type,
950            unpacking_alignment,
951            alpha_treatment: pixels.alpha_treatment,
952            y_axis_treatment: pixels.y_axis_treatment,
953            pixel_format: pixels.pixel_format,
954            data: pixels.data.into(),
955        });
956    }
957
958    fn get_gl_extensions(&self) -> String {
959        let (sender, receiver) = webgl_channel().unwrap();
960        self.send_command(WebGLCommand::GetExtensions(sender));
961        receiver.recv().unwrap()
962    }
963
964    // https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/
965    pub(crate) fn draw_arrays_instanced(
966        &self,
967        mode: u32,
968        first: i32,
969        count: i32,
970        primcount: i32,
971    ) -> WebGLResult<()> {
972        match mode {
973            constants::POINTS |
974            constants::LINE_STRIP |
975            constants::LINE_LOOP |
976            constants::LINES |
977            constants::TRIANGLE_STRIP |
978            constants::TRIANGLE_FAN |
979            constants::TRIANGLES => {},
980            _ => {
981                return Err(InvalidEnum);
982            },
983        }
984        if first < 0 || count < 0 || primcount < 0 {
985            return Err(InvalidValue);
986        }
987
988        let current_program = self.current_program.get().ok_or(InvalidOperation)?;
989
990        let required_len = if count > 0 {
991            first
992                .checked_add(count)
993                .map(|len| len as u32)
994                .ok_or(InvalidOperation)?
995        } else {
996            0
997        };
998
999        match self.webgl_version() {
1000            WebGLVersion::WebGL1 => self.current_vao().validate_for_draw(
1001                required_len,
1002                primcount as u32,
1003                &current_program.active_attribs(),
1004            )?,
1005            WebGLVersion::WebGL2 => self.current_vao_webgl2().validate_for_draw(
1006                required_len,
1007                primcount as u32,
1008                &current_program.active_attribs(),
1009            )?,
1010        };
1011
1012        self.validate_framebuffer()?;
1013
1014        if count == 0 || primcount == 0 {
1015            return Ok(());
1016        }
1017
1018        self.send_command(if primcount == 1 {
1019            WebGLCommand::DrawArrays { mode, first, count }
1020        } else {
1021            WebGLCommand::DrawArraysInstanced {
1022                mode,
1023                first,
1024                count,
1025                primcount,
1026            }
1027        });
1028        self.mark_as_dirty();
1029        Ok(())
1030    }
1031
1032    // https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/
1033    pub(crate) fn draw_elements_instanced(
1034        &self,
1035        mode: u32,
1036        count: i32,
1037        type_: u32,
1038        offset: i64,
1039        primcount: i32,
1040    ) -> WebGLResult<()> {
1041        match mode {
1042            constants::POINTS |
1043            constants::LINE_STRIP |
1044            constants::LINE_LOOP |
1045            constants::LINES |
1046            constants::TRIANGLE_STRIP |
1047            constants::TRIANGLE_FAN |
1048            constants::TRIANGLES => {},
1049            _ => {
1050                return Err(InvalidEnum);
1051            },
1052        }
1053        if count < 0 || offset < 0 || primcount < 0 {
1054            return Err(InvalidValue);
1055        }
1056        let type_size = match type_ {
1057            constants::UNSIGNED_BYTE => 1,
1058            constants::UNSIGNED_SHORT => 2,
1059            constants::UNSIGNED_INT => match self.webgl_version() {
1060                WebGLVersion::WebGL1 if self.extension_manager.is_element_index_uint_enabled() => 4,
1061                WebGLVersion::WebGL2 => 4,
1062                _ => return Err(InvalidEnum),
1063            },
1064            _ => return Err(InvalidEnum),
1065        };
1066        if offset % type_size != 0 {
1067            return Err(InvalidOperation);
1068        }
1069
1070        let current_program = self.current_program.get().ok_or(InvalidOperation)?;
1071        let array_buffer = match self.webgl_version() {
1072            WebGLVersion::WebGL1 => self.current_vao().element_array_buffer().get(),
1073            WebGLVersion::WebGL2 => self.current_vao_webgl2().element_array_buffer().get(),
1074        }
1075        .ok_or(InvalidOperation)?;
1076
1077        if count > 0 && primcount > 0 {
1078            // This operation cannot overflow in u64 and we know all those values are nonnegative.
1079            let val = offset as u64 + (count as u64 * type_size as u64);
1080            if val > array_buffer.capacity() as u64 {
1081                return Err(InvalidOperation);
1082            }
1083        }
1084
1085        // TODO(nox): Pass the correct number of vertices required.
1086        match self.webgl_version() {
1087            WebGLVersion::WebGL1 => self.current_vao().validate_for_draw(
1088                0,
1089                primcount as u32,
1090                &current_program.active_attribs(),
1091            )?,
1092            WebGLVersion::WebGL2 => self.current_vao_webgl2().validate_for_draw(
1093                0,
1094                primcount as u32,
1095                &current_program.active_attribs(),
1096            )?,
1097        };
1098
1099        self.validate_framebuffer()?;
1100
1101        if count == 0 || primcount == 0 {
1102            return Ok(());
1103        }
1104
1105        let offset = offset as u32;
1106        self.send_command(if primcount == 1 {
1107            WebGLCommand::DrawElements {
1108                mode,
1109                count,
1110                type_,
1111                offset,
1112            }
1113        } else {
1114            WebGLCommand::DrawElementsInstanced {
1115                mode,
1116                count,
1117                type_,
1118                offset,
1119                primcount,
1120            }
1121        });
1122        self.mark_as_dirty();
1123        Ok(())
1124    }
1125
1126    pub(crate) fn vertex_attrib_divisor(&self, index: u32, divisor: u32) {
1127        if index >= self.limits.max_vertex_attribs {
1128            return self.webgl_error(InvalidValue);
1129        }
1130
1131        match self.webgl_version() {
1132            WebGLVersion::WebGL1 => self.current_vao().vertex_attrib_divisor(index, divisor),
1133            WebGLVersion::WebGL2 => self
1134                .current_vao_webgl2()
1135                .vertex_attrib_divisor(index, divisor),
1136        };
1137        self.send_command(WebGLCommand::VertexAttribDivisor { index, divisor });
1138    }
1139
1140    pub(crate) fn array_buffer(&self) -> Option<DomRoot<WebGLBuffer>> {
1141        self.bound_buffer_array.get()
1142    }
1143
1144    pub(crate) fn array_buffer_slot(&self) -> &MutNullableDom<WebGLBuffer> {
1145        &self.bound_buffer_array
1146    }
1147
1148    pub(crate) fn bound_buffer(&self, target: u32) -> WebGLResult<Option<DomRoot<WebGLBuffer>>> {
1149        match target {
1150            constants::ARRAY_BUFFER => Ok(self.bound_buffer_array.get()),
1151            constants::ELEMENT_ARRAY_BUFFER => Ok(self.current_vao().element_array_buffer().get()),
1152            _ => Err(WebGLError::InvalidEnum),
1153        }
1154    }
1155
1156    pub(crate) fn buffer_usage(&self, usage: u32) -> WebGLResult<u32> {
1157        match usage {
1158            constants::STREAM_DRAW | constants::STATIC_DRAW | constants::DYNAMIC_DRAW => Ok(usage),
1159            _ => Err(WebGLError::InvalidEnum),
1160        }
1161    }
1162
1163    pub(crate) fn create_vertex_array(&self) -> Option<DomRoot<WebGLVertexArrayObjectOES>> {
1164        let (sender, receiver) = webgl_channel().unwrap();
1165        self.send_command(WebGLCommand::CreateVertexArray(sender));
1166        receiver
1167            .recv()
1168            .unwrap()
1169            .map(|id| WebGLVertexArrayObjectOES::new(self, Some(id), CanGc::note()))
1170    }
1171
1172    pub(crate) fn create_vertex_array_webgl2(&self) -> Option<DomRoot<WebGLVertexArrayObject>> {
1173        let (sender, receiver) = webgl_channel().unwrap();
1174        self.send_command(WebGLCommand::CreateVertexArray(sender));
1175        receiver
1176            .recv()
1177            .unwrap()
1178            .map(|id| WebGLVertexArrayObject::new(self, Some(id), CanGc::note()))
1179    }
1180
1181    pub(crate) fn delete_vertex_array(&self, vao: Option<&WebGLVertexArrayObjectOES>) {
1182        if let Some(vao) = vao {
1183            handle_potential_webgl_error!(self, self.validate_ownership(vao), return);
1184            // The default vertex array has no id and should never be passed around.
1185            assert!(vao.id().is_some());
1186            if vao.is_deleted() {
1187                return;
1188            }
1189            if vao == &*self.current_vao() {
1190                // Setting it to None will make self.current_vao() reset it to the default one
1191                // next time it is called.
1192                self.current_vao.set(None);
1193                self.send_command(WebGLCommand::BindVertexArray(None));
1194            }
1195            vao.delete(Operation::Infallible);
1196        }
1197    }
1198
1199    pub(crate) fn delete_vertex_array_webgl2(&self, vao: Option<&WebGLVertexArrayObject>) {
1200        if let Some(vao) = vao {
1201            handle_potential_webgl_error!(self, self.validate_ownership(vao), return);
1202            // The default vertex array has no id and should never be passed around.
1203            assert!(vao.id().is_some());
1204            if vao.is_deleted() {
1205                return;
1206            }
1207            if vao == &*self.current_vao_webgl2() {
1208                // Setting it to None will make self.current_vao() reset it to the default one
1209                // next time it is called.
1210                self.current_vao_webgl2.set(None);
1211                self.send_command(WebGLCommand::BindVertexArray(None));
1212            }
1213            vao.delete(Operation::Infallible);
1214        }
1215    }
1216
1217    pub(crate) fn is_vertex_array(&self, vao: Option<&WebGLVertexArrayObjectOES>) -> bool {
1218        vao.is_some_and(|vao| {
1219            // The default vertex array has no id and should never be passed around.
1220            assert!(vao.id().is_some());
1221            self.validate_ownership(vao).is_ok() && vao.ever_bound() && !vao.is_deleted()
1222        })
1223    }
1224
1225    pub(crate) fn is_vertex_array_webgl2(&self, vao: Option<&WebGLVertexArrayObject>) -> bool {
1226        vao.is_some_and(|vao| {
1227            // The default vertex array has no id and should never be passed around.
1228            assert!(vao.id().is_some());
1229            self.validate_ownership(vao).is_ok() && vao.ever_bound() && !vao.is_deleted()
1230        })
1231    }
1232
1233    pub(crate) fn bind_vertex_array(&self, vao: Option<&WebGLVertexArrayObjectOES>) {
1234        if let Some(vao) = vao {
1235            // The default vertex array has no id and should never be passed around.
1236            assert!(vao.id().is_some());
1237            handle_potential_webgl_error!(self, self.validate_ownership(vao), return);
1238            if vao.is_deleted() {
1239                return self.webgl_error(InvalidOperation);
1240            }
1241            vao.set_ever_bound();
1242        }
1243        self.send_command(WebGLCommand::BindVertexArray(vao.and_then(|vao| vao.id())));
1244        // Setting it to None will make self.current_vao() reset it to the default one
1245        // next time it is called.
1246        self.current_vao.set(vao);
1247    }
1248
1249    pub(crate) fn bind_vertex_array_webgl2(&self, vao: Option<&WebGLVertexArrayObject>) {
1250        if let Some(vao) = vao {
1251            // The default vertex array has no id and should never be passed around.
1252            assert!(vao.id().is_some());
1253            handle_potential_webgl_error!(self, self.validate_ownership(vao), return);
1254            if vao.is_deleted() {
1255                return self.webgl_error(InvalidOperation);
1256            }
1257            vao.set_ever_bound();
1258        }
1259        self.send_command(WebGLCommand::BindVertexArray(vao.and_then(|vao| vao.id())));
1260        // Setting it to None will make self.current_vao() reset it to the default one
1261        // next time it is called.
1262        self.current_vao_webgl2.set(vao);
1263    }
1264
1265    fn validate_blend_mode(&self, mode: u32) -> WebGLResult<()> {
1266        match mode {
1267            constants::FUNC_ADD | constants::FUNC_SUBTRACT | constants::FUNC_REVERSE_SUBTRACT => {
1268                Ok(())
1269            },
1270            EXTBlendMinmaxConstants::MIN_EXT | EXTBlendMinmaxConstants::MAX_EXT
1271                if self.extension_manager.is_blend_minmax_enabled() =>
1272            {
1273                Ok(())
1274            },
1275            _ => Err(InvalidEnum),
1276        }
1277    }
1278
1279    pub(crate) fn initialize_framebuffer(&self, clear_bits: u32) {
1280        if clear_bits == 0 {
1281            return;
1282        }
1283        self.send_command(WebGLCommand::InitializeFramebuffer {
1284            color: clear_bits & constants::COLOR_BUFFER_BIT != 0,
1285            depth: clear_bits & constants::DEPTH_BUFFER_BIT != 0,
1286            stencil: clear_bits & constants::STENCIL_BUFFER_BIT != 0,
1287        });
1288    }
1289
1290    pub(crate) fn extension_manager(&self) -> &WebGLExtensions {
1291        &self.extension_manager
1292    }
1293
1294    #[allow(unsafe_code)]
1295    pub(crate) fn buffer_data(
1296        &self,
1297        target: u32,
1298        data: Option<ArrayBufferViewOrArrayBuffer>,
1299        usage: u32,
1300        bound_buffer: Option<DomRoot<WebGLBuffer>>,
1301    ) {
1302        let data = handle_potential_webgl_error!(self, data.ok_or(InvalidValue), return);
1303        let bound_buffer =
1304            handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return);
1305
1306        let data = unsafe {
1307            // Safe because we don't do anything with JS until the end of the method.
1308            match data {
1309                ArrayBufferViewOrArrayBuffer::ArrayBuffer(ref data) => data.as_slice(),
1310                ArrayBufferViewOrArrayBuffer::ArrayBufferView(ref data) => data.as_slice(),
1311            }
1312        };
1313        handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, data, usage));
1314    }
1315
1316    pub(crate) fn buffer_data_(
1317        &self,
1318        target: u32,
1319        size: i64,
1320        usage: u32,
1321        bound_buffer: Option<DomRoot<WebGLBuffer>>,
1322    ) {
1323        let bound_buffer =
1324            handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return);
1325
1326        if size < 0 {
1327            return self.webgl_error(InvalidValue);
1328        }
1329
1330        // FIXME: Allocating a buffer based on user-requested size is
1331        // not great, but we don't have a fallible allocation to try.
1332        let data = vec![0u8; size as usize];
1333        handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, &data, usage));
1334    }
1335
1336    #[allow(unsafe_code)]
1337    pub(crate) fn buffer_sub_data(
1338        &self,
1339        target: u32,
1340        offset: i64,
1341        data: ArrayBufferViewOrArrayBuffer,
1342        bound_buffer: Option<DomRoot<WebGLBuffer>>,
1343    ) {
1344        let bound_buffer =
1345            handle_potential_webgl_error!(self, bound_buffer.ok_or(InvalidOperation), return);
1346
1347        if offset < 0 {
1348            return self.webgl_error(InvalidValue);
1349        }
1350
1351        let data = unsafe {
1352            // Safe because we don't do anything with JS until the end of the method.
1353            match data {
1354                ArrayBufferViewOrArrayBuffer::ArrayBuffer(ref data) => data.as_slice(),
1355                ArrayBufferViewOrArrayBuffer::ArrayBufferView(ref data) => data.as_slice(),
1356            }
1357        };
1358        if (offset as u64) + data.len() as u64 > bound_buffer.capacity() as u64 {
1359            return self.webgl_error(InvalidValue);
1360        }
1361        let (sender, receiver) = ipc::bytes_channel().unwrap();
1362        self.send_command(WebGLCommand::BufferSubData(
1363            target,
1364            offset as isize,
1365            receiver,
1366        ));
1367        sender.send(data).unwrap();
1368    }
1369
1370    pub(crate) fn bind_buffer_maybe(
1371        &self,
1372        slot: &MutNullableDom<WebGLBuffer>,
1373        target: u32,
1374        buffer: Option<&WebGLBuffer>,
1375    ) {
1376        if let Some(buffer) = buffer {
1377            handle_potential_webgl_error!(self, self.validate_ownership(buffer), return);
1378
1379            if buffer.is_marked_for_deletion() {
1380                return self.webgl_error(InvalidOperation);
1381            }
1382            handle_potential_webgl_error!(self, buffer.set_target_maybe(target), return);
1383            buffer.increment_attached_counter();
1384        }
1385
1386        self.send_command(WebGLCommand::BindBuffer(target, buffer.map(|b| b.id())));
1387        if let Some(old) = slot.get() {
1388            old.decrement_attached_counter(Operation::Infallible);
1389        }
1390
1391        slot.set(buffer);
1392    }
1393
1394    pub(crate) fn current_program(&self) -> Option<DomRoot<WebGLProgram>> {
1395        self.current_program.get()
1396    }
1397
1398    pub(crate) fn uniform_check_program(
1399        &self,
1400        program: &WebGLProgram,
1401        location: &WebGLUniformLocation,
1402    ) -> WebGLResult<()> {
1403        self.validate_ownership(program)?;
1404
1405        if program.is_deleted() ||
1406            !program.is_linked() ||
1407            self.context_id() != location.context_id() ||
1408            program.id() != location.program_id() ||
1409            program.link_generation() != location.link_generation()
1410        {
1411            return Err(InvalidOperation);
1412        }
1413
1414        Ok(())
1415    }
1416
1417    fn uniform_vec_section_int(
1418        &self,
1419        vec: Int32ArrayOrLongSequence,
1420        offset: u32,
1421        length: u32,
1422        uniform_size: usize,
1423        uniform_location: &WebGLUniformLocation,
1424    ) -> WebGLResult<Vec<i32>> {
1425        let vec = match vec {
1426            Int32ArrayOrLongSequence::Int32Array(v) => v.to_vec(),
1427            Int32ArrayOrLongSequence::LongSequence(v) => v,
1428        };
1429        self.uniform_vec_section::<i32>(vec, offset, length, uniform_size, uniform_location)
1430    }
1431
1432    fn uniform_vec_section_float(
1433        &self,
1434        vec: Float32ArrayOrUnrestrictedFloatSequence,
1435        offset: u32,
1436        length: u32,
1437        uniform_size: usize,
1438        uniform_location: &WebGLUniformLocation,
1439    ) -> WebGLResult<Vec<f32>> {
1440        let vec = match vec {
1441            Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
1442            Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
1443        };
1444        self.uniform_vec_section::<f32>(vec, offset, length, uniform_size, uniform_location)
1445    }
1446
1447    pub(crate) fn uniform_vec_section<T: Clone>(
1448        &self,
1449        vec: Vec<T>,
1450        offset: u32,
1451        length: u32,
1452        uniform_size: usize,
1453        uniform_location: &WebGLUniformLocation,
1454    ) -> WebGLResult<Vec<T>> {
1455        let offset = offset as usize;
1456        if offset > vec.len() {
1457            return Err(InvalidValue);
1458        }
1459
1460        let length = if length > 0 {
1461            length as usize
1462        } else {
1463            vec.len() - offset
1464        };
1465        if offset + length > vec.len() {
1466            return Err(InvalidValue);
1467        }
1468
1469        let vec = if offset == 0 && length == vec.len() {
1470            vec
1471        } else {
1472            vec[offset..offset + length].to_vec()
1473        };
1474
1475        if vec.len() < uniform_size || vec.len() % uniform_size != 0 {
1476            return Err(InvalidValue);
1477        }
1478        if uniform_location.size().is_none() && vec.len() != uniform_size {
1479            return Err(InvalidOperation);
1480        }
1481
1482        Ok(vec)
1483    }
1484
1485    pub(crate) fn uniform_matrix_section(
1486        &self,
1487        vec: Float32ArrayOrUnrestrictedFloatSequence,
1488        offset: u32,
1489        length: u32,
1490        transpose: bool,
1491        uniform_size: usize,
1492        uniform_location: &WebGLUniformLocation,
1493    ) -> WebGLResult<Vec<f32>> {
1494        let vec = match vec {
1495            Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
1496            Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
1497        };
1498        if transpose {
1499            return Err(InvalidValue);
1500        }
1501        self.uniform_vec_section::<f32>(vec, offset, length, uniform_size, uniform_location)
1502    }
1503
1504    pub(crate) fn get_draw_framebuffer_slot(&self) -> &MutNullableDom<WebGLFramebuffer> {
1505        &self.bound_draw_framebuffer
1506    }
1507
1508    pub(crate) fn get_read_framebuffer_slot(&self) -> &MutNullableDom<WebGLFramebuffer> {
1509        &self.bound_read_framebuffer
1510    }
1511
1512    pub(crate) fn validate_new_framebuffer_binding(
1513        &self,
1514        framebuffer: Option<&WebGLFramebuffer>,
1515    ) -> WebGLResult<()> {
1516        if let Some(fb) = framebuffer {
1517            self.validate_ownership(fb)?;
1518            if fb.is_deleted() {
1519                // From the WebGL spec:
1520                //
1521                //     "An attempt to bind a deleted framebuffer will
1522                //      generate an INVALID_OPERATION error, and the
1523                //      current binding will remain untouched."
1524                return Err(InvalidOperation);
1525            }
1526        }
1527        Ok(())
1528    }
1529
1530    pub(crate) fn bind_framebuffer_to(
1531        &self,
1532        target: u32,
1533        framebuffer: Option<&WebGLFramebuffer>,
1534        slot: &MutNullableDom<WebGLFramebuffer>,
1535    ) {
1536        match framebuffer {
1537            Some(framebuffer) => framebuffer.bind(target),
1538            None => {
1539                // Bind the default framebuffer
1540                let cmd =
1541                    WebGLCommand::BindFramebuffer(target, WebGLFramebufferBindingRequest::Default);
1542                self.send_command(cmd);
1543            },
1544        }
1545        slot.set(framebuffer);
1546    }
1547
1548    pub(crate) fn renderbuffer_storage(
1549        &self,
1550        target: u32,
1551        samples: i32,
1552        internal_format: u32,
1553        width: i32,
1554        height: i32,
1555    ) {
1556        if target != constants::RENDERBUFFER {
1557            return self.webgl_error(InvalidEnum);
1558        }
1559
1560        let max = self.limits.max_renderbuffer_size;
1561
1562        if samples < 0 || width < 0 || width as u32 > max || height < 0 || height as u32 > max {
1563            return self.webgl_error(InvalidValue);
1564        }
1565
1566        let rb = handle_potential_webgl_error!(
1567            self,
1568            self.bound_renderbuffer.get().ok_or(InvalidOperation),
1569            return
1570        );
1571        handle_potential_webgl_error!(
1572            self,
1573            rb.storage(self.api_type, samples, internal_format, width, height)
1574        );
1575        if let Some(fb) = self.bound_draw_framebuffer.get() {
1576            fb.invalidate_renderbuffer(&rb);
1577        }
1578
1579        // FIXME: https://github.com/servo/servo/issues/13710
1580    }
1581
1582    pub(crate) fn valid_color_attachment_enum(&self, attachment: u32) -> bool {
1583        let last_slot = constants::COLOR_ATTACHMENT0 + self.limits().max_color_attachments - 1;
1584        constants::COLOR_ATTACHMENT0 <= attachment && attachment <= last_slot
1585    }
1586
1587    #[allow(clippy::too_many_arguments)]
1588    pub(crate) fn compressed_tex_image_2d(
1589        &self,
1590        target: u32,
1591        level: i32,
1592        internal_format: u32,
1593        width: i32,
1594        height: i32,
1595        border: i32,
1596        data: &[u8],
1597    ) {
1598        let validator = CompressedTexImage2DValidator::new(
1599            self,
1600            target,
1601            level,
1602            width,
1603            height,
1604            border,
1605            internal_format,
1606            data.len(),
1607        );
1608        let CommonCompressedTexImage2DValidatorResult {
1609            texture,
1610            target,
1611            level,
1612            width,
1613            height,
1614            compression,
1615        } = match validator.validate() {
1616            Ok(result) => result,
1617            Err(_) => return,
1618        };
1619
1620        if texture.is_immutable() {
1621            return self.webgl_error(InvalidOperation);
1622        }
1623
1624        let size = Size2D::new(width, height);
1625        let data = IpcSharedMemory::from_bytes(data);
1626
1627        handle_potential_webgl_error!(
1628            self,
1629            texture.initialize(
1630                target,
1631                size.width,
1632                size.height,
1633                1,
1634                compression.format,
1635                level,
1636                Some(TexDataType::UnsignedByte)
1637            )
1638        );
1639
1640        self.send_command(WebGLCommand::CompressedTexImage2D {
1641            target: target.as_gl_constant(),
1642            level,
1643            internal_format,
1644            size: Size2D::new(width, height),
1645            data: data.into(),
1646        });
1647
1648        if let Some(fb) = self.bound_draw_framebuffer.get() {
1649            fb.invalidate_texture(&texture);
1650        }
1651    }
1652
1653    #[allow(clippy::too_many_arguments)]
1654    pub(crate) fn compressed_tex_sub_image_2d(
1655        &self,
1656        target: u32,
1657        level: i32,
1658        xoffset: i32,
1659        yoffset: i32,
1660        width: i32,
1661        height: i32,
1662        format: u32,
1663        data: &[u8],
1664    ) {
1665        let validator = CompressedTexSubImage2DValidator::new(
1666            self,
1667            target,
1668            level,
1669            xoffset,
1670            yoffset,
1671            width,
1672            height,
1673            format,
1674            data.len(),
1675        );
1676        let CommonCompressedTexImage2DValidatorResult {
1677            texture: _,
1678            target,
1679            level,
1680            width,
1681            height,
1682            ..
1683        } = match validator.validate() {
1684            Ok(result) => result,
1685            Err(_) => return,
1686        };
1687
1688        let data = IpcSharedMemory::from_bytes(data);
1689
1690        self.send_command(WebGLCommand::CompressedTexSubImage2D {
1691            target: target.as_gl_constant(),
1692            level: level as i32,
1693            xoffset,
1694            yoffset,
1695            size: Size2D::new(width, height),
1696            format,
1697            data: data.into(),
1698        });
1699    }
1700
1701    pub(crate) fn uniform1iv(
1702        &self,
1703        location: Option<&WebGLUniformLocation>,
1704        val: Int32ArrayOrLongSequence,
1705        src_offset: u32,
1706        src_length: u32,
1707    ) {
1708        self.with_location(location, |location| {
1709            match location.type_() {
1710                constants::BOOL |
1711                constants::INT |
1712                constants::SAMPLER_2D |
1713                WebGL2RenderingContextConstants::SAMPLER_2D_ARRAY |
1714                WebGL2RenderingContextConstants::SAMPLER_3D |
1715                constants::SAMPLER_CUBE => {},
1716                _ => return Err(InvalidOperation),
1717            }
1718
1719            let val = self.uniform_vec_section_int(val, src_offset, src_length, 1, location)?;
1720
1721            match location.type_() {
1722                constants::SAMPLER_2D |
1723                constants::SAMPLER_CUBE |
1724                WebGL2RenderingContextConstants::SAMPLER_2D_ARRAY |
1725                WebGL2RenderingContextConstants::SAMPLER_3D => {
1726                    for &v in val
1727                        .iter()
1728                        .take(cmp::min(location.size().unwrap_or(1) as usize, val.len()))
1729                    {
1730                        if v < 0 || v as u32 >= self.limits.max_combined_texture_image_units {
1731                            return Err(InvalidValue);
1732                        }
1733                    }
1734                },
1735                _ => {},
1736            }
1737            self.send_command(WebGLCommand::Uniform1iv(location.id(), val));
1738            Ok(())
1739        });
1740    }
1741
1742    pub(crate) fn uniform1fv(
1743        &self,
1744        location: Option<&WebGLUniformLocation>,
1745        val: Float32ArrayOrUnrestrictedFloatSequence,
1746        src_offset: u32,
1747        src_length: u32,
1748    ) {
1749        self.with_location(location, |location| {
1750            match location.type_() {
1751                constants::BOOL | constants::FLOAT => {},
1752                _ => return Err(InvalidOperation),
1753            }
1754            let val = self.uniform_vec_section_float(val, src_offset, src_length, 1, location)?;
1755            self.send_command(WebGLCommand::Uniform1fv(location.id(), val));
1756            Ok(())
1757        });
1758    }
1759
1760    pub(crate) fn uniform2fv(
1761        &self,
1762        location: Option<&WebGLUniformLocation>,
1763        val: Float32ArrayOrUnrestrictedFloatSequence,
1764        src_offset: u32,
1765        src_length: u32,
1766    ) {
1767        self.with_location(location, |location| {
1768            match location.type_() {
1769                constants::BOOL_VEC2 | constants::FLOAT_VEC2 => {},
1770                _ => return Err(InvalidOperation),
1771            }
1772            let val = self.uniform_vec_section_float(val, src_offset, src_length, 2, location)?;
1773            self.send_command(WebGLCommand::Uniform2fv(location.id(), val));
1774            Ok(())
1775        });
1776    }
1777
1778    pub(crate) fn uniform2iv(
1779        &self,
1780        location: Option<&WebGLUniformLocation>,
1781        val: Int32ArrayOrLongSequence,
1782        src_offset: u32,
1783        src_length: u32,
1784    ) {
1785        self.with_location(location, |location| {
1786            match location.type_() {
1787                constants::BOOL_VEC2 | constants::INT_VEC2 => {},
1788                _ => return Err(InvalidOperation),
1789            }
1790            let val = self.uniform_vec_section_int(val, src_offset, src_length, 2, location)?;
1791            self.send_command(WebGLCommand::Uniform2iv(location.id(), val));
1792            Ok(())
1793        });
1794    }
1795
1796    pub(crate) fn uniform3fv(
1797        &self,
1798        location: Option<&WebGLUniformLocation>,
1799        val: Float32ArrayOrUnrestrictedFloatSequence,
1800        src_offset: u32,
1801        src_length: u32,
1802    ) {
1803        self.with_location(location, |location| {
1804            match location.type_() {
1805                constants::BOOL_VEC3 | constants::FLOAT_VEC3 => {},
1806                _ => return Err(InvalidOperation),
1807            }
1808            let val = self.uniform_vec_section_float(val, src_offset, src_length, 3, location)?;
1809            self.send_command(WebGLCommand::Uniform3fv(location.id(), val));
1810            Ok(())
1811        });
1812    }
1813
1814    pub(crate) fn uniform3iv(
1815        &self,
1816        location: Option<&WebGLUniformLocation>,
1817        val: Int32ArrayOrLongSequence,
1818        src_offset: u32,
1819        src_length: u32,
1820    ) {
1821        self.with_location(location, |location| {
1822            match location.type_() {
1823                constants::BOOL_VEC3 | constants::INT_VEC3 => {},
1824                _ => return Err(InvalidOperation),
1825            }
1826            let val = self.uniform_vec_section_int(val, src_offset, src_length, 3, location)?;
1827            self.send_command(WebGLCommand::Uniform3iv(location.id(), val));
1828            Ok(())
1829        });
1830    }
1831
1832    pub(crate) fn uniform4iv(
1833        &self,
1834        location: Option<&WebGLUniformLocation>,
1835        val: Int32ArrayOrLongSequence,
1836        src_offset: u32,
1837        src_length: u32,
1838    ) {
1839        self.with_location(location, |location| {
1840            match location.type_() {
1841                constants::BOOL_VEC4 | constants::INT_VEC4 => {},
1842                _ => return Err(InvalidOperation),
1843            }
1844            let val = self.uniform_vec_section_int(val, src_offset, src_length, 4, location)?;
1845            self.send_command(WebGLCommand::Uniform4iv(location.id(), val));
1846            Ok(())
1847        });
1848    }
1849
1850    pub(crate) fn uniform4fv(
1851        &self,
1852        location: Option<&WebGLUniformLocation>,
1853        val: Float32ArrayOrUnrestrictedFloatSequence,
1854        src_offset: u32,
1855        src_length: u32,
1856    ) {
1857        self.with_location(location, |location| {
1858            match location.type_() {
1859                constants::BOOL_VEC4 | constants::FLOAT_VEC4 => {},
1860                _ => return Err(InvalidOperation),
1861            }
1862            let val = self.uniform_vec_section_float(val, src_offset, src_length, 4, location)?;
1863            self.send_command(WebGLCommand::Uniform4fv(location.id(), val));
1864            Ok(())
1865        });
1866    }
1867
1868    pub(crate) fn uniform_matrix_2fv(
1869        &self,
1870        location: Option<&WebGLUniformLocation>,
1871        transpose: bool,
1872        val: Float32ArrayOrUnrestrictedFloatSequence,
1873        src_offset: u32,
1874        src_length: u32,
1875    ) {
1876        self.with_location(location, |location| {
1877            match location.type_() {
1878                constants::FLOAT_MAT2 => {},
1879                _ => return Err(InvalidOperation),
1880            }
1881            let val =
1882                self.uniform_matrix_section(val, src_offset, src_length, transpose, 4, location)?;
1883            self.send_command(WebGLCommand::UniformMatrix2fv(location.id(), val));
1884            Ok(())
1885        });
1886    }
1887
1888    pub(crate) fn uniform_matrix_3fv(
1889        &self,
1890        location: Option<&WebGLUniformLocation>,
1891        transpose: bool,
1892        val: Float32ArrayOrUnrestrictedFloatSequence,
1893        src_offset: u32,
1894        src_length: u32,
1895    ) {
1896        self.with_location(location, |location| {
1897            match location.type_() {
1898                constants::FLOAT_MAT3 => {},
1899                _ => return Err(InvalidOperation),
1900            }
1901            let val =
1902                self.uniform_matrix_section(val, src_offset, src_length, transpose, 9, location)?;
1903            self.send_command(WebGLCommand::UniformMatrix3fv(location.id(), val));
1904            Ok(())
1905        });
1906    }
1907
1908    pub(crate) fn uniform_matrix_4fv(
1909        &self,
1910        location: Option<&WebGLUniformLocation>,
1911        transpose: bool,
1912        val: Float32ArrayOrUnrestrictedFloatSequence,
1913        src_offset: u32,
1914        src_length: u32,
1915    ) {
1916        self.with_location(location, |location| {
1917            match location.type_() {
1918                constants::FLOAT_MAT4 => {},
1919                _ => return Err(InvalidOperation),
1920            }
1921            let val =
1922                self.uniform_matrix_section(val, src_offset, src_length, transpose, 16, location)?;
1923            self.send_command(WebGLCommand::UniformMatrix4fv(location.id(), val));
1924            Ok(())
1925        });
1926    }
1927
1928    pub(crate) fn get_buffer_param(
1929        &self,
1930        buffer: Option<DomRoot<WebGLBuffer>>,
1931        parameter: u32,
1932        mut retval: MutableHandleValue,
1933    ) {
1934        let buffer = handle_potential_webgl_error!(
1935            self,
1936            buffer.ok_or(InvalidOperation),
1937            return retval.set(NullValue())
1938        );
1939
1940        retval.set(match parameter {
1941            constants::BUFFER_SIZE => Int32Value(buffer.capacity() as i32),
1942            constants::BUFFER_USAGE => Int32Value(buffer.usage() as i32),
1943            _ => {
1944                self.webgl_error(InvalidEnum);
1945                NullValue()
1946            },
1947        })
1948    }
1949}
1950
1951impl CanvasContext for WebGLRenderingContext {
1952    type ID = WebGLContextId;
1953
1954    fn context_id(&self) -> Self::ID {
1955        self.webgl_sender.context_id()
1956    }
1957
1958    fn canvas(&self) -> Option<RootedHTMLCanvasElementOrOffscreenCanvas> {
1959        Some(RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas))
1960    }
1961
1962    fn resize(&self) {
1963        let size = self.size().cast();
1964        let (sender, receiver) = webgl_channel().unwrap();
1965        self.webgl_sender.send_resize(size, sender).unwrap();
1966        // FIXME(#21718) The backend is allowed to choose a size smaller than
1967        // what was requested
1968        self.size.set(size);
1969
1970        if let Err(msg) = receiver.recv().unwrap() {
1971            error!("Error resizing WebGLContext: {}", msg);
1972            return;
1973        };
1974
1975        // ClearColor needs to be restored because after a resize the GLContext is recreated
1976        // and the framebuffer is cleared using the default black transparent color.
1977        let color = self.current_clear_color.get();
1978        self.send_command(WebGLCommand::ClearColor(color.0, color.1, color.2, color.3));
1979
1980        // WebGL Spec: Scissor rect must not change if the canvas is resized.
1981        // See: webgl/conformance-1.0.3/conformance/rendering/gl-scissor-canvas-dimensions.html
1982        // NativeContext handling library changes the scissor after a resize, so we need to reset the
1983        // default scissor when the canvas was created or the last scissor that the user set.
1984        let rect = self.current_scissor.get();
1985        self.send_command(WebGLCommand::Scissor(rect.0, rect.1, rect.2, rect.3));
1986
1987        // Bound texture must not change when the canvas is resized.
1988        // Right now surfman generates a new FBO and the bound texture is changed
1989        // in order to create a new render to texture attachment.
1990        // Send a command to re-bind the TEXTURE_2D, if any.
1991        if let Some(texture) = self
1992            .textures
1993            .active_texture_slot(constants::TEXTURE_2D, self.webgl_version())
1994            .unwrap()
1995            .get()
1996        {
1997            self.send_command(WebGLCommand::BindTexture(
1998                constants::TEXTURE_2D,
1999                Some(texture.id()),
2000            ));
2001        }
2002
2003        // Bound framebuffer must not change when the canvas is resized.
2004        // Right now surfman generates a new FBO on resize.
2005        // Send a command to re-bind the framebuffer, if any.
2006        if let Some(fbo) = self.bound_draw_framebuffer.get() {
2007            let id = WebGLFramebufferBindingRequest::Explicit(fbo.id());
2008            self.send_command(WebGLCommand::BindFramebuffer(constants::FRAMEBUFFER, id));
2009        }
2010    }
2011
2012    fn reset_bitmap(&self) {
2013        warn!("The WebGLRenderingContext 'reset_bitmap' is not implemented yet");
2014    }
2015
2016    // Used by HTMLCanvasElement.toDataURL
2017    //
2018    // This emits errors quite liberally, but the spec says that this operation
2019    // can fail and that it is UB what happens in that case.
2020    //
2021    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#2.2>
2022    fn get_image_data(&self) -> Option<Snapshot> {
2023        handle_potential_webgl_error!(self, self.validate_framebuffer(), return None);
2024        let mut size = self.size().cast();
2025
2026        let (fb_width, fb_height) = handle_potential_webgl_error!(
2027            self,
2028            self.get_current_framebuffer_size().ok_or(InvalidOperation),
2029            return None
2030        );
2031        size.width = cmp::min(size.width, fb_width as u32);
2032        size.height = cmp::min(size.height, fb_height as u32);
2033
2034        let (sender, receiver) = ipc::channel().unwrap();
2035        self.send_command(WebGLCommand::ReadPixels(
2036            Rect::from_size(size),
2037            constants::RGBA,
2038            constants::UNSIGNED_BYTE,
2039            sender,
2040        ));
2041        let (data, alpha_mode) = receiver.recv().unwrap();
2042        Some(Snapshot::from_vec(
2043            size.cast(),
2044            SnapshotPixelFormat::RGBA,
2045            alpha_mode,
2046            data.to_vec(),
2047        ))
2048    }
2049
2050    fn mark_as_dirty(&self) {
2051        // If we have a bound framebuffer, then don't mark the canvas as dirty.
2052        if self.bound_draw_framebuffer.get().is_some() {
2053            return;
2054        }
2055
2056        // Dirtying the canvas is unnecessary if we're actively displaying immersive
2057        // XR content right now.
2058        if self.global().as_window().in_immersive_xr_session() {
2059            return;
2060        }
2061
2062        self.canvas.mark_as_dirty();
2063    }
2064}
2065
2066#[cfg(not(feature = "webgl_backtrace"))]
2067#[inline]
2068pub(crate) fn capture_webgl_backtrace() -> WebGLCommandBacktrace {
2069    WebGLCommandBacktrace {}
2070}
2071
2072#[cfg(feature = "webgl_backtrace")]
2073#[cfg_attr(feature = "webgl_backtrace", allow(unsafe_code))]
2074pub(crate) fn capture_webgl_backtrace() -> WebGLCommandBacktrace {
2075    let bt = Backtrace::new();
2076    unsafe {
2077        capture_stack!(in(*GlobalScope::get_cx()) let stack);
2078        WebGLCommandBacktrace {
2079            backtrace: format!("{:?}", bt),
2080            js_backtrace: stack.and_then(|s| s.as_string(None, js::jsapi::StackFormat::Default)),
2081        }
2082    }
2083}
2084
2085impl Drop for WebGLRenderingContext {
2086    fn drop(&mut self) {
2087        let _ = self.webgl_sender.send_remove();
2088    }
2089}
2090
2091impl WebGLRenderingContextMethods<crate::DomTypeHolder> for WebGLRenderingContext {
2092    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1>
2093    fn Canvas(&self) -> RootedHTMLCanvasElementOrOffscreenCanvas {
2094        RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas)
2095    }
2096
2097    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11>
2098    fn Flush(&self) {
2099        self.send_command(WebGLCommand::Flush);
2100    }
2101
2102    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11>
2103    fn Finish(&self) {
2104        let (sender, receiver) = webgl_channel().unwrap();
2105        self.send_command(WebGLCommand::Finish(sender));
2106        receiver.recv().unwrap()
2107    }
2108
2109    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1>
2110    fn DrawingBufferWidth(&self) -> i32 {
2111        let (sender, receiver) = webgl_channel().unwrap();
2112        self.send_command(WebGLCommand::DrawingBufferWidth(sender));
2113        receiver.recv().unwrap()
2114    }
2115
2116    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1>
2117    fn DrawingBufferHeight(&self) -> i32 {
2118        let (sender, receiver) = webgl_channel().unwrap();
2119        self.send_command(WebGLCommand::DrawingBufferHeight(sender));
2120        receiver.recv().unwrap()
2121    }
2122
2123    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
2124    fn GetBufferParameter(
2125        &self,
2126        _cx: SafeJSContext,
2127        target: u32,
2128        parameter: u32,
2129        mut retval: MutableHandleValue,
2130    ) {
2131        let buffer = handle_potential_webgl_error!(
2132            self,
2133            self.bound_buffer(target),
2134            return retval.set(NullValue())
2135        );
2136        self.get_buffer_param(buffer, parameter, retval)
2137    }
2138
2139    #[allow(unsafe_code)]
2140    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2141    fn GetParameter(&self, cx: SafeJSContext, parameter: u32, mut retval: MutableHandleValue) {
2142        if !self
2143            .extension_manager
2144            .is_get_parameter_name_enabled(parameter)
2145        {
2146            self.webgl_error(WebGLError::InvalidEnum);
2147            return retval.set(NullValue());
2148        }
2149
2150        match parameter {
2151            constants::ARRAY_BUFFER_BINDING => {
2152                self.bound_buffer_array
2153                    .get()
2154                    .safe_to_jsval(cx, retval, CanGc::note());
2155                return;
2156            },
2157            constants::CURRENT_PROGRAM => {
2158                self.current_program
2159                    .get()
2160                    .safe_to_jsval(cx, retval, CanGc::note());
2161                return;
2162            },
2163            constants::ELEMENT_ARRAY_BUFFER_BINDING => {
2164                let buffer = self.current_vao().element_array_buffer().get();
2165                buffer.safe_to_jsval(cx, retval, CanGc::note());
2166                return;
2167            },
2168            constants::FRAMEBUFFER_BINDING => {
2169                self.bound_draw_framebuffer
2170                    .get()
2171                    .safe_to_jsval(cx, retval, CanGc::note());
2172                return;
2173            },
2174            constants::RENDERBUFFER_BINDING => {
2175                self.bound_renderbuffer
2176                    .get()
2177                    .safe_to_jsval(cx, retval, CanGc::note());
2178                return;
2179            },
2180            constants::TEXTURE_BINDING_2D => {
2181                let texture = self
2182                    .textures
2183                    .active_texture_slot(constants::TEXTURE_2D, self.webgl_version())
2184                    .unwrap()
2185                    .get();
2186                texture.safe_to_jsval(cx, retval, CanGc::note());
2187                return;
2188            },
2189            WebGL2RenderingContextConstants::TEXTURE_BINDING_2D_ARRAY => {
2190                let texture = self
2191                    .textures
2192                    .active_texture_slot(
2193                        WebGL2RenderingContextConstants::TEXTURE_2D_ARRAY,
2194                        self.webgl_version(),
2195                    )
2196                    .unwrap()
2197                    .get();
2198                texture.safe_to_jsval(cx, retval, CanGc::note());
2199                return;
2200            },
2201            WebGL2RenderingContextConstants::TEXTURE_BINDING_3D => {
2202                let texture = self
2203                    .textures
2204                    .active_texture_slot(
2205                        WebGL2RenderingContextConstants::TEXTURE_3D,
2206                        self.webgl_version(),
2207                    )
2208                    .unwrap()
2209                    .get();
2210                texture.safe_to_jsval(cx, retval, CanGc::note());
2211                return;
2212            },
2213            constants::TEXTURE_BINDING_CUBE_MAP => {
2214                let texture = self
2215                    .textures
2216                    .active_texture_slot(constants::TEXTURE_CUBE_MAP, self.webgl_version())
2217                    .unwrap()
2218                    .get();
2219                texture.safe_to_jsval(cx, retval, CanGc::note());
2220                return;
2221            },
2222            OESVertexArrayObjectConstants::VERTEX_ARRAY_BINDING_OES => {
2223                let vao = self.current_vao.get().filter(|vao| vao.id().is_some());
2224                vao.safe_to_jsval(cx, retval, CanGc::note());
2225                return;
2226            },
2227            // In readPixels we currently support RGBA/UBYTE only.  If
2228            // we wanted to support other formats, we could ask the
2229            // driver, but we would need to check for
2230            // GL_OES_read_format support (assuming an underlying GLES
2231            // driver. Desktop is happy to format convert for us).
2232            constants::IMPLEMENTATION_COLOR_READ_FORMAT => {
2233                if self.validate_framebuffer().is_err() {
2234                    self.webgl_error(InvalidOperation);
2235                    return retval.set(NullValue());
2236                }
2237                return retval.set(Int32Value(constants::RGBA as i32));
2238            },
2239            constants::IMPLEMENTATION_COLOR_READ_TYPE => {
2240                if self.validate_framebuffer().is_err() {
2241                    self.webgl_error(InvalidOperation);
2242                    return retval.set(NullValue());
2243                }
2244                return retval.set(Int32Value(constants::UNSIGNED_BYTE as i32));
2245            },
2246            constants::COMPRESSED_TEXTURE_FORMATS => unsafe {
2247                let format_ids = self.extension_manager.get_tex_compression_ids();
2248
2249                rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
2250                Uint32Array::create(*cx, CreateWith::Slice(&format_ids), rval.handle_mut())
2251                    .unwrap();
2252                return retval.set(ObjectValue(rval.get()));
2253            },
2254            constants::VERSION => {
2255                "WebGL 1.0".safe_to_jsval(cx, retval, CanGc::note());
2256                return;
2257            },
2258            constants::RENDERER | constants::VENDOR => {
2259                "Mozilla/Servo".safe_to_jsval(cx, retval, CanGc::note());
2260                return;
2261            },
2262            constants::SHADING_LANGUAGE_VERSION => {
2263                "WebGL GLSL ES 1.0".safe_to_jsval(cx, retval, CanGc::note());
2264                return;
2265            },
2266            constants::UNPACK_FLIP_Y_WEBGL => {
2267                let unpack = self.texture_unpacking_settings.get();
2268                retval.set(BooleanValue(unpack.contains(TextureUnpacking::FLIP_Y_AXIS)));
2269                return;
2270            },
2271            constants::UNPACK_PREMULTIPLY_ALPHA_WEBGL => {
2272                let unpack = self.texture_unpacking_settings.get();
2273                retval.set(BooleanValue(
2274                    unpack.contains(TextureUnpacking::PREMULTIPLY_ALPHA),
2275                ));
2276                return;
2277            },
2278            constants::PACK_ALIGNMENT => {
2279                retval.set(UInt32Value(self.texture_packing_alignment.get() as u32));
2280                return;
2281            },
2282            constants::UNPACK_ALIGNMENT => {
2283                retval.set(UInt32Value(self.texture_unpacking_alignment.get()));
2284                return;
2285            },
2286            constants::UNPACK_COLORSPACE_CONVERSION_WEBGL => {
2287                let unpack = self.texture_unpacking_settings.get();
2288                retval.set(UInt32Value(
2289                    if unpack.contains(TextureUnpacking::CONVERT_COLORSPACE) {
2290                        constants::BROWSER_DEFAULT_WEBGL
2291                    } else {
2292                        constants::NONE
2293                    },
2294                ));
2295                return;
2296            },
2297            _ => {},
2298        }
2299
2300        // Handle any MAX_ parameters by retrieving the limits that were stored
2301        // when this context was created.
2302        let limit = match parameter {
2303            constants::MAX_VERTEX_ATTRIBS => Some(self.limits.max_vertex_attribs),
2304            constants::MAX_TEXTURE_SIZE => Some(self.limits.max_tex_size),
2305            constants::MAX_CUBE_MAP_TEXTURE_SIZE => Some(self.limits.max_cube_map_tex_size),
2306            constants::MAX_COMBINED_TEXTURE_IMAGE_UNITS => {
2307                Some(self.limits.max_combined_texture_image_units)
2308            },
2309            constants::MAX_FRAGMENT_UNIFORM_VECTORS => {
2310                Some(self.limits.max_fragment_uniform_vectors)
2311            },
2312            constants::MAX_RENDERBUFFER_SIZE => Some(self.limits.max_renderbuffer_size),
2313            constants::MAX_TEXTURE_IMAGE_UNITS => Some(self.limits.max_texture_image_units),
2314            constants::MAX_VARYING_VECTORS => Some(self.limits.max_varying_vectors),
2315            constants::MAX_VERTEX_TEXTURE_IMAGE_UNITS => {
2316                Some(self.limits.max_vertex_texture_image_units)
2317            },
2318            constants::MAX_VERTEX_UNIFORM_VECTORS => Some(self.limits.max_vertex_uniform_vectors),
2319            _ => None,
2320        };
2321        if let Some(limit) = limit {
2322            retval.set(UInt32Value(limit));
2323            return;
2324        }
2325
2326        if let Ok(value) = self.capabilities.is_enabled(parameter) {
2327            retval.set(BooleanValue(value));
2328            return;
2329        }
2330
2331        match handle_potential_webgl_error!(
2332            self,
2333            Parameter::from_u32(parameter),
2334            return retval.set(NullValue())
2335        ) {
2336            Parameter::Bool(param) => {
2337                let (sender, receiver) = webgl_channel().unwrap();
2338                self.send_command(WebGLCommand::GetParameterBool(param, sender));
2339                retval.set(BooleanValue(receiver.recv().unwrap()))
2340            },
2341            Parameter::Bool4(param) => {
2342                let (sender, receiver) = webgl_channel().unwrap();
2343                self.send_command(WebGLCommand::GetParameterBool4(param, sender));
2344                receiver
2345                    .recv()
2346                    .unwrap()
2347                    .safe_to_jsval(cx, retval, CanGc::note());
2348            },
2349            Parameter::Int(param) => {
2350                let (sender, receiver) = webgl_channel().unwrap();
2351                self.send_command(WebGLCommand::GetParameterInt(param, sender));
2352                retval.set(Int32Value(receiver.recv().unwrap()))
2353            },
2354            Parameter::Int2(param) => unsafe {
2355                let (sender, receiver) = webgl_channel().unwrap();
2356                self.send_command(WebGLCommand::GetParameterInt2(param, sender));
2357                rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
2358                Int32Array::create(
2359                    *cx,
2360                    CreateWith::Slice(&receiver.recv().unwrap()),
2361                    rval.handle_mut(),
2362                )
2363                .unwrap();
2364                retval.set(ObjectValue(rval.get()))
2365            },
2366            Parameter::Int4(param) => unsafe {
2367                let (sender, receiver) = webgl_channel().unwrap();
2368                self.send_command(WebGLCommand::GetParameterInt4(param, sender));
2369                rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
2370                Int32Array::create(
2371                    *cx,
2372                    CreateWith::Slice(&receiver.recv().unwrap()),
2373                    rval.handle_mut(),
2374                )
2375                .unwrap();
2376                retval.set(ObjectValue(rval.get()))
2377            },
2378            Parameter::Float(param) => {
2379                let (sender, receiver) = webgl_channel().unwrap();
2380                self.send_command(WebGLCommand::GetParameterFloat(param, sender));
2381                retval.set(DoubleValue(receiver.recv().unwrap() as f64))
2382            },
2383            Parameter::Float2(param) => unsafe {
2384                let (sender, receiver) = webgl_channel().unwrap();
2385                self.send_command(WebGLCommand::GetParameterFloat2(param, sender));
2386                rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
2387                Float32Array::create(
2388                    *cx,
2389                    CreateWith::Slice(&receiver.recv().unwrap()),
2390                    rval.handle_mut(),
2391                )
2392                .unwrap();
2393                retval.set(ObjectValue(rval.get()))
2394            },
2395            Parameter::Float4(param) => unsafe {
2396                let (sender, receiver) = webgl_channel().unwrap();
2397                self.send_command(WebGLCommand::GetParameterFloat4(param, sender));
2398                rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
2399                Float32Array::create(
2400                    *cx,
2401                    CreateWith::Slice(&receiver.recv().unwrap()),
2402                    rval.handle_mut(),
2403                )
2404                .unwrap();
2405                retval.set(ObjectValue(rval.get()))
2406            },
2407        }
2408    }
2409
2410    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
2411    fn GetTexParameter(
2412        &self,
2413        _cx: SafeJSContext,
2414        target: u32,
2415        pname: u32,
2416        mut retval: MutableHandleValue,
2417    ) {
2418        let texture_slot = handle_potential_webgl_error!(
2419            self,
2420            self.textures
2421                .active_texture_slot(target, self.webgl_version()),
2422            return retval.set(NullValue())
2423        );
2424        let texture = handle_potential_webgl_error!(
2425            self,
2426            texture_slot.get().ok_or(InvalidOperation),
2427            return retval.set(NullValue())
2428        );
2429
2430        if !self
2431            .extension_manager
2432            .is_get_tex_parameter_name_enabled(pname)
2433        {
2434            self.webgl_error(InvalidEnum);
2435            return retval.set(NullValue());
2436        }
2437
2438        match pname {
2439            constants::TEXTURE_MAG_FILTER => return retval.set(UInt32Value(texture.mag_filter())),
2440            constants::TEXTURE_MIN_FILTER => return retval.set(UInt32Value(texture.min_filter())),
2441            _ => {},
2442        }
2443
2444        let texparam = handle_potential_webgl_error!(
2445            self,
2446            TexParameter::from_u32(pname),
2447            return retval.set(NullValue())
2448        );
2449        if self.webgl_version() < texparam.required_webgl_version() {
2450            self.webgl_error(InvalidEnum);
2451            return retval.set(NullValue());
2452        }
2453
2454        if let Some(value) = texture.maybe_get_tex_parameter(texparam) {
2455            match value {
2456                TexParameterValue::Float(v) => retval.set(DoubleValue(v as f64)),
2457                TexParameterValue::Int(v) => retval.set(Int32Value(v)),
2458                TexParameterValue::Bool(v) => retval.set(BooleanValue(v)),
2459            }
2460            return;
2461        }
2462
2463        match texparam {
2464            TexParameter::Float(param) => {
2465                let (sender, receiver) = webgl_channel().unwrap();
2466                self.send_command(WebGLCommand::GetTexParameterFloat(target, param, sender));
2467                retval.set(DoubleValue(receiver.recv().unwrap() as f64))
2468            },
2469            TexParameter::Int(param) => {
2470                let (sender, receiver) = webgl_channel().unwrap();
2471                self.send_command(WebGLCommand::GetTexParameterInt(target, param, sender));
2472                retval.set(Int32Value(receiver.recv().unwrap()))
2473            },
2474            TexParameter::Bool(param) => {
2475                let (sender, receiver) = webgl_channel().unwrap();
2476                self.send_command(WebGLCommand::GetTexParameterBool(target, param, sender));
2477                retval.set(BooleanValue(receiver.recv().unwrap()))
2478            },
2479        }
2480    }
2481
2482    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2483    fn GetError(&self) -> u32 {
2484        let error_code = if let Some(error) = self.last_error.get() {
2485            match error {
2486                WebGLError::InvalidEnum => constants::INVALID_ENUM,
2487                WebGLError::InvalidFramebufferOperation => constants::INVALID_FRAMEBUFFER_OPERATION,
2488                WebGLError::InvalidValue => constants::INVALID_VALUE,
2489                WebGLError::InvalidOperation => constants::INVALID_OPERATION,
2490                WebGLError::OutOfMemory => constants::OUT_OF_MEMORY,
2491                WebGLError::ContextLost => constants::CONTEXT_LOST_WEBGL,
2492            }
2493        } else {
2494            constants::NO_ERROR
2495        };
2496        self.last_error.set(None);
2497        error_code
2498    }
2499
2500    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.2>
2501    fn GetContextAttributes(&self) -> Option<WebGLContextAttributes> {
2502        let (sender, receiver) = webgl_channel().unwrap();
2503
2504        // If the send does not succeed, assume context lost
2505        let backtrace = capture_webgl_backtrace();
2506        if self
2507            .webgl_sender
2508            .send(WebGLCommand::GetContextAttributes(sender), backtrace)
2509            .is_err()
2510        {
2511            return None;
2512        }
2513
2514        let attrs = receiver.recv().unwrap();
2515
2516        Some(WebGLContextAttributes {
2517            alpha: attrs.alpha,
2518            antialias: attrs.antialias,
2519            depth: attrs.depth,
2520            failIfMajorPerformanceCaveat: false,
2521            preferLowPowerToHighPerformance: false,
2522            premultipliedAlpha: attrs.premultiplied_alpha,
2523            preserveDrawingBuffer: attrs.preserve_drawing_buffer,
2524            stencil: attrs.stencil,
2525        })
2526    }
2527
2528    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.13>
2529    fn IsContextLost(&self) -> bool {
2530        false
2531    }
2532
2533    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.14>
2534    fn GetSupportedExtensions(&self) -> Option<Vec<DOMString>> {
2535        self.extension_manager
2536            .init_once(|| self.get_gl_extensions());
2537        let extensions = self.extension_manager.get_supported_extensions();
2538        Some(
2539            extensions
2540                .iter()
2541                .map(|name| DOMString::from(*name))
2542                .collect(),
2543        )
2544    }
2545
2546    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.14>
2547    fn GetExtension(&self, _cx: SafeJSContext, name: DOMString) -> Option<NonNull<JSObject>> {
2548        self.extension_manager
2549            .init_once(|| self.get_gl_extensions());
2550        self.extension_manager.get_or_init_extension(&name, self)
2551    }
2552
2553    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2554    fn ActiveTexture(&self, texture: u32) {
2555        handle_potential_webgl_error!(self, self.textures.set_active_unit_enum(texture), return);
2556        self.send_command(WebGLCommand::ActiveTexture(texture));
2557    }
2558
2559    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2560    fn BlendColor(&self, r: f32, g: f32, b: f32, a: f32) {
2561        self.send_command(WebGLCommand::BlendColor(r, g, b, a));
2562    }
2563
2564    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2565    fn BlendEquation(&self, mode: u32) {
2566        handle_potential_webgl_error!(self, self.validate_blend_mode(mode), return);
2567        self.send_command(WebGLCommand::BlendEquation(mode))
2568    }
2569
2570    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2571    fn BlendEquationSeparate(&self, mode_rgb: u32, mode_alpha: u32) {
2572        handle_potential_webgl_error!(self, self.validate_blend_mode(mode_rgb), return);
2573        handle_potential_webgl_error!(self, self.validate_blend_mode(mode_alpha), return);
2574        self.send_command(WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha));
2575    }
2576
2577    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2578    fn BlendFunc(&self, src_factor: u32, dest_factor: u32) {
2579        // From the WebGL 1.0 spec, 6.13: Viewport Depth Range:
2580        //
2581        //     A call to blendFunc will generate an INVALID_OPERATION error if one of the two
2582        //     factors is set to CONSTANT_COLOR or ONE_MINUS_CONSTANT_COLOR and the other to
2583        //     CONSTANT_ALPHA or ONE_MINUS_CONSTANT_ALPHA.
2584        if has_invalid_blend_constants(src_factor, dest_factor) {
2585            return self.webgl_error(InvalidOperation);
2586        }
2587        if has_invalid_blend_constants(dest_factor, src_factor) {
2588            return self.webgl_error(InvalidOperation);
2589        }
2590
2591        self.send_command(WebGLCommand::BlendFunc(src_factor, dest_factor));
2592    }
2593
2594    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2595    fn BlendFuncSeparate(&self, src_rgb: u32, dest_rgb: u32, src_alpha: u32, dest_alpha: u32) {
2596        // From the WebGL 1.0 spec, 6.13: Viewport Depth Range:
2597        //
2598        //     A call to blendFuncSeparate will generate an INVALID_OPERATION error if srcRGB is
2599        //     set to CONSTANT_COLOR or ONE_MINUS_CONSTANT_COLOR and dstRGB is set to
2600        //     CONSTANT_ALPHA or ONE_MINUS_CONSTANT_ALPHA or vice versa.
2601        if has_invalid_blend_constants(src_rgb, dest_rgb) {
2602            return self.webgl_error(InvalidOperation);
2603        }
2604        if has_invalid_blend_constants(dest_rgb, src_rgb) {
2605            return self.webgl_error(InvalidOperation);
2606        }
2607
2608        self.send_command(WebGLCommand::BlendFuncSeparate(
2609            src_rgb, dest_rgb, src_alpha, dest_alpha,
2610        ));
2611    }
2612
2613    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2614    fn AttachShader(&self, program: &WebGLProgram, shader: &WebGLShader) {
2615        handle_potential_webgl_error!(self, self.validate_ownership(program), return);
2616        handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
2617        handle_potential_webgl_error!(self, program.attach_shader(shader));
2618    }
2619
2620    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2621    fn DetachShader(&self, program: &WebGLProgram, shader: &WebGLShader) {
2622        handle_potential_webgl_error!(self, self.validate_ownership(program), return);
2623        handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
2624        handle_potential_webgl_error!(self, program.detach_shader(shader));
2625    }
2626
2627    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
2628    fn BindAttribLocation(&self, program: &WebGLProgram, index: u32, name: DOMString) {
2629        handle_potential_webgl_error!(self, self.validate_ownership(program), return);
2630        handle_potential_webgl_error!(self, program.bind_attrib_location(index, name));
2631    }
2632
2633    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
2634    fn BindBuffer(&self, target: u32, buffer: Option<&WebGLBuffer>) {
2635        let current_vao;
2636        let slot = match target {
2637            constants::ARRAY_BUFFER => &self.bound_buffer_array,
2638            constants::ELEMENT_ARRAY_BUFFER => {
2639                current_vao = self.current_vao();
2640                current_vao.element_array_buffer()
2641            },
2642            _ => return self.webgl_error(InvalidEnum),
2643        };
2644        self.bind_buffer_maybe(slot, target, buffer);
2645    }
2646
2647    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
2648    fn BindFramebuffer(&self, target: u32, framebuffer: Option<&WebGLFramebuffer>) {
2649        handle_potential_webgl_error!(
2650            self,
2651            self.validate_new_framebuffer_binding(framebuffer),
2652            return
2653        );
2654
2655        if target != constants::FRAMEBUFFER {
2656            return self.webgl_error(InvalidEnum);
2657        }
2658
2659        self.bind_framebuffer_to(target, framebuffer, &self.bound_draw_framebuffer)
2660    }
2661
2662    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
2663    fn BindRenderbuffer(&self, target: u32, renderbuffer: Option<&WebGLRenderbuffer>) {
2664        if let Some(rb) = renderbuffer {
2665            handle_potential_webgl_error!(self, self.validate_ownership(rb), return);
2666        }
2667
2668        if target != constants::RENDERBUFFER {
2669            return self.webgl_error(InvalidEnum);
2670        }
2671
2672        match renderbuffer {
2673            // Implementations differ on what to do in the deleted
2674            // case: Chromium currently unbinds, and Gecko silently
2675            // returns.  The conformance tests don't cover this case.
2676            Some(renderbuffer) if !renderbuffer.is_deleted() => {
2677                self.bound_renderbuffer.set(Some(renderbuffer));
2678                renderbuffer.bind(target);
2679            },
2680            _ => {
2681                if renderbuffer.is_some() {
2682                    self.webgl_error(InvalidOperation);
2683                }
2684
2685                self.bound_renderbuffer.set(None);
2686                // Unbind the currently bound renderbuffer
2687                self.send_command(WebGLCommand::BindRenderbuffer(target, None));
2688            },
2689        }
2690    }
2691
2692    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
2693    fn BindTexture(&self, target: u32, texture: Option<&WebGLTexture>) {
2694        if let Some(texture) = texture {
2695            handle_potential_webgl_error!(self, self.validate_ownership(texture), return);
2696        }
2697
2698        let texture_slot = handle_potential_webgl_error!(
2699            self,
2700            self.textures
2701                .active_texture_slot(target, self.webgl_version()),
2702            return
2703        );
2704
2705        if let Some(texture) = texture {
2706            handle_potential_webgl_error!(self, texture.bind(target), return);
2707        } else {
2708            self.send_command(WebGLCommand::BindTexture(target, None));
2709        }
2710        texture_slot.set(texture);
2711    }
2712
2713    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
2714    fn GenerateMipmap(&self, target: u32) {
2715        let texture_slot = handle_potential_webgl_error!(
2716            self,
2717            self.textures
2718                .active_texture_slot(target, self.webgl_version()),
2719            return
2720        );
2721        let texture =
2722            handle_potential_webgl_error!(self, texture_slot.get().ok_or(InvalidOperation), return);
2723        handle_potential_webgl_error!(self, texture.generate_mipmap());
2724    }
2725
2726    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
2727    fn BufferData_(&self, target: u32, data: Option<ArrayBufferViewOrArrayBuffer>, usage: u32) {
2728        let usage = handle_potential_webgl_error!(self, self.buffer_usage(usage), return);
2729        let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return);
2730        self.buffer_data(target, data, usage, bound_buffer)
2731    }
2732
2733    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
2734    fn BufferData(&self, target: u32, size: i64, usage: u32) {
2735        let usage = handle_potential_webgl_error!(self, self.buffer_usage(usage), return);
2736        let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return);
2737        self.buffer_data_(target, size, usage, bound_buffer)
2738    }
2739
2740    // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
2741    #[allow(unsafe_code)]
2742    fn BufferSubData(&self, target: u32, offset: i64, data: ArrayBufferViewOrArrayBuffer) {
2743        let bound_buffer = handle_potential_webgl_error!(self, self.bound_buffer(target), return);
2744        self.buffer_sub_data(target, offset, data, bound_buffer)
2745    }
2746
2747    // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
2748    #[allow(unsafe_code)]
2749    fn CompressedTexImage2D(
2750        &self,
2751        target: u32,
2752        level: i32,
2753        internal_format: u32,
2754        width: i32,
2755        height: i32,
2756        border: i32,
2757        data: CustomAutoRooterGuard<ArrayBufferView>,
2758    ) {
2759        let data = unsafe { data.as_slice() };
2760        self.compressed_tex_image_2d(target, level, internal_format, width, height, border, data)
2761    }
2762
2763    // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
2764    #[allow(unsafe_code)]
2765    fn CompressedTexSubImage2D(
2766        &self,
2767        target: u32,
2768        level: i32,
2769        xoffset: i32,
2770        yoffset: i32,
2771        width: i32,
2772        height: i32,
2773        format: u32,
2774        data: CustomAutoRooterGuard<ArrayBufferView>,
2775    ) {
2776        let data = unsafe { data.as_slice() };
2777        self.compressed_tex_sub_image_2d(
2778            target, level, xoffset, yoffset, width, height, format, data,
2779        )
2780    }
2781
2782    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
2783    fn CopyTexImage2D(
2784        &self,
2785        target: u32,
2786        level: i32,
2787        internal_format: u32,
2788        x: i32,
2789        y: i32,
2790        width: i32,
2791        height: i32,
2792        border: i32,
2793    ) {
2794        handle_potential_webgl_error!(self, self.validate_framebuffer(), return);
2795
2796        let validator = CommonTexImage2DValidator::new(
2797            self,
2798            target,
2799            level,
2800            internal_format,
2801            width,
2802            height,
2803            border,
2804        );
2805        let CommonTexImage2DValidatorResult {
2806            texture,
2807            target,
2808            level,
2809            internal_format,
2810            width,
2811            height,
2812            border,
2813        } = match validator.validate() {
2814            Ok(result) => result,
2815            Err(_) => return,
2816        };
2817
2818        if texture.is_immutable() {
2819            return self.webgl_error(InvalidOperation);
2820        }
2821
2822        let framebuffer_format = match self.bound_draw_framebuffer.get() {
2823            Some(fb) => match fb.attachment(constants::COLOR_ATTACHMENT0) {
2824                Some(WebGLFramebufferAttachmentRoot::Renderbuffer(rb)) => {
2825                    TexFormat::from_gl_constant(rb.internal_format())
2826                },
2827                Some(WebGLFramebufferAttachmentRoot::Texture(texture)) => texture
2828                    .image_info_for_target(&target, 0)
2829                    .map(|info| info.internal_format()),
2830                None => None,
2831            },
2832            None => {
2833                let attrs = self.GetContextAttributes().unwrap();
2834                Some(if attrs.alpha {
2835                    TexFormat::RGBA
2836                } else {
2837                    TexFormat::RGB
2838                })
2839            },
2840        };
2841
2842        let framebuffer_format = match framebuffer_format {
2843            Some(f) => f,
2844            None => {
2845                self.webgl_error(InvalidOperation);
2846                return;
2847            },
2848        };
2849
2850        match (framebuffer_format, internal_format) {
2851            (a, b) if a == b => (),
2852            (TexFormat::RGBA, TexFormat::RGB) => (),
2853            (TexFormat::RGBA, TexFormat::Alpha) => (),
2854            (TexFormat::RGBA, TexFormat::Luminance) => (),
2855            (TexFormat::RGBA, TexFormat::LuminanceAlpha) => (),
2856            (TexFormat::RGB, TexFormat::Luminance) => (),
2857            _ => {
2858                self.webgl_error(InvalidOperation);
2859                return;
2860            },
2861        }
2862
2863        // NB: TexImage2D depth is always equal to 1
2864        handle_potential_webgl_error!(
2865            self,
2866            texture.initialize(target, width, height, 1, internal_format, level, None)
2867        );
2868
2869        let msg = WebGLCommand::CopyTexImage2D(
2870            target.as_gl_constant(),
2871            level as i32,
2872            internal_format.as_gl_constant(),
2873            x,
2874            y,
2875            width as i32,
2876            height as i32,
2877            border as i32,
2878        );
2879
2880        self.send_command(msg);
2881    }
2882
2883    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
2884    fn CopyTexSubImage2D(
2885        &self,
2886        target: u32,
2887        level: i32,
2888        xoffset: i32,
2889        yoffset: i32,
2890        x: i32,
2891        y: i32,
2892        width: i32,
2893        height: i32,
2894    ) {
2895        handle_potential_webgl_error!(self, self.validate_framebuffer(), return);
2896
2897        // NB: We use a dummy (valid) format and border in order to reuse the
2898        // common validations, but this should have its own validator.
2899        let validator = CommonTexImage2DValidator::new(
2900            self,
2901            target,
2902            level,
2903            TexFormat::RGBA.as_gl_constant(),
2904            width,
2905            height,
2906            0,
2907        );
2908        let CommonTexImage2DValidatorResult {
2909            texture,
2910            target,
2911            level,
2912            width,
2913            height,
2914            ..
2915        } = match validator.validate() {
2916            Ok(result) => result,
2917            Err(_) => return,
2918        };
2919
2920        let image_info = match texture.image_info_for_target(&target, level) {
2921            Some(info) => info,
2922            None => return self.webgl_error(InvalidOperation),
2923        };
2924
2925        // GL_INVALID_VALUE is generated if:
2926        //   - xoffset or yoffset is less than 0
2927        //   - x offset plus the width is greater than the texture width
2928        //   - y offset plus the height is greater than the texture height
2929        if xoffset < 0 ||
2930            (xoffset as u32 + width) > image_info.width() ||
2931            yoffset < 0 ||
2932            (yoffset as u32 + height) > image_info.height()
2933        {
2934            self.webgl_error(InvalidValue);
2935            return;
2936        }
2937
2938        let msg = WebGLCommand::CopyTexSubImage2D(
2939            target.as_gl_constant(),
2940            level as i32,
2941            xoffset,
2942            yoffset,
2943            x,
2944            y,
2945            width as i32,
2946            height as i32,
2947        );
2948
2949        self.send_command(msg);
2950    }
2951
2952    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11>
2953    fn Clear(&self, mask: u32) {
2954        handle_potential_webgl_error!(self, self.validate_framebuffer(), return);
2955        if mask &
2956            !(constants::DEPTH_BUFFER_BIT |
2957                constants::STENCIL_BUFFER_BIT |
2958                constants::COLOR_BUFFER_BIT) !=
2959            0
2960        {
2961            return self.webgl_error(InvalidValue);
2962        }
2963
2964        self.send_command(WebGLCommand::Clear(mask));
2965        self.mark_as_dirty();
2966    }
2967
2968    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2969    fn ClearColor(&self, red: f32, green: f32, blue: f32, alpha: f32) {
2970        self.current_clear_color.set((red, green, blue, alpha));
2971        self.send_command(WebGLCommand::ClearColor(red, green, blue, alpha));
2972    }
2973
2974    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2975    fn ClearDepth(&self, depth: f32) {
2976        self.send_command(WebGLCommand::ClearDepth(depth))
2977    }
2978
2979    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2980    fn ClearStencil(&self, stencil: i32) {
2981        self.send_command(WebGLCommand::ClearStencil(stencil))
2982    }
2983
2984    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2985    fn ColorMask(&self, r: bool, g: bool, b: bool, a: bool) {
2986        self.send_command(WebGLCommand::ColorMask(r, g, b, a))
2987    }
2988
2989    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
2990    fn CullFace(&self, mode: u32) {
2991        match mode {
2992            constants::FRONT | constants::BACK | constants::FRONT_AND_BACK => {
2993                self.send_command(WebGLCommand::CullFace(mode))
2994            },
2995            _ => self.webgl_error(InvalidEnum),
2996        }
2997    }
2998
2999    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
3000    fn FrontFace(&self, mode: u32) {
3001        match mode {
3002            constants::CW | constants::CCW => self.send_command(WebGLCommand::FrontFace(mode)),
3003            _ => self.webgl_error(InvalidEnum),
3004        }
3005    }
3006    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
3007    fn DepthFunc(&self, func: u32) {
3008        match func {
3009            constants::NEVER |
3010            constants::LESS |
3011            constants::EQUAL |
3012            constants::LEQUAL |
3013            constants::GREATER |
3014            constants::NOTEQUAL |
3015            constants::GEQUAL |
3016            constants::ALWAYS => self.send_command(WebGLCommand::DepthFunc(func)),
3017            _ => self.webgl_error(InvalidEnum),
3018        }
3019    }
3020
3021    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
3022    fn DepthMask(&self, flag: bool) {
3023        self.send_command(WebGLCommand::DepthMask(flag))
3024    }
3025
3026    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
3027    fn DepthRange(&self, near: f32, far: f32) {
3028        // https://www.khronos.org/registry/webgl/specs/latest/1.0/#VIEWPORT_DEPTH_RANGE
3029        if near > far {
3030            return self.webgl_error(InvalidOperation);
3031        }
3032        self.send_command(WebGLCommand::DepthRange(near, far))
3033    }
3034
3035    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
3036    fn Enable(&self, cap: u32) {
3037        if handle_potential_webgl_error!(self, self.capabilities.set(cap, true), return) {
3038            self.send_command(WebGLCommand::Enable(cap));
3039        }
3040    }
3041
3042    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
3043    fn Disable(&self, cap: u32) {
3044        if handle_potential_webgl_error!(self, self.capabilities.set(cap, false), return) {
3045            self.send_command(WebGLCommand::Disable(cap));
3046        }
3047    }
3048
3049    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
3050    fn CompileShader(&self, shader: &WebGLShader) {
3051        handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
3052        handle_potential_webgl_error!(
3053            self,
3054            shader.compile(
3055                self.api_type,
3056                self.webgl_version,
3057                self.glsl_version,
3058                &self.limits,
3059                &self.extension_manager,
3060            )
3061        )
3062    }
3063
3064    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
3065    fn CreateBuffer(&self) -> Option<DomRoot<WebGLBuffer>> {
3066        WebGLBuffer::maybe_new(self, CanGc::note())
3067    }
3068
3069    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
3070    fn CreateFramebuffer(&self) -> Option<DomRoot<WebGLFramebuffer>> {
3071        WebGLFramebuffer::maybe_new(self, CanGc::note())
3072    }
3073
3074    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
3075    fn CreateRenderbuffer(&self) -> Option<DomRoot<WebGLRenderbuffer>> {
3076        WebGLRenderbuffer::maybe_new(self)
3077    }
3078
3079    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
3080    fn CreateTexture(&self) -> Option<DomRoot<WebGLTexture>> {
3081        WebGLTexture::maybe_new(self)
3082    }
3083
3084    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
3085    fn CreateProgram(&self) -> Option<DomRoot<WebGLProgram>> {
3086        WebGLProgram::maybe_new(self, CanGc::note())
3087    }
3088
3089    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
3090    fn CreateShader(&self, shader_type: u32) -> Option<DomRoot<WebGLShader>> {
3091        match shader_type {
3092            constants::VERTEX_SHADER | constants::FRAGMENT_SHADER => {},
3093            _ => {
3094                self.webgl_error(InvalidEnum);
3095                return None;
3096            },
3097        }
3098        WebGLShader::maybe_new(self, shader_type)
3099    }
3100
3101    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
3102    fn DeleteBuffer(&self, buffer: Option<&WebGLBuffer>) {
3103        let buffer = match buffer {
3104            Some(buffer) => buffer,
3105            None => return,
3106        };
3107        handle_potential_webgl_error!(self, self.validate_ownership(buffer), return);
3108        if buffer.is_marked_for_deletion() {
3109            return;
3110        }
3111        self.current_vao().unbind_buffer(buffer);
3112        if self.bound_buffer_array.get().is_some_and(|b| buffer == &*b) {
3113            self.bound_buffer_array.set(None);
3114            buffer.decrement_attached_counter(Operation::Infallible);
3115        }
3116        buffer.mark_for_deletion(Operation::Infallible);
3117    }
3118
3119    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
3120    fn DeleteFramebuffer(&self, framebuffer: Option<&WebGLFramebuffer>) {
3121        if let Some(framebuffer) = framebuffer {
3122            // https://immersive-web.github.io/webxr/#opaque-framebuffer
3123            // Can opaque framebuffers be deleted?
3124            // https://github.com/immersive-web/webxr/issues/855
3125            handle_potential_webgl_error!(self, framebuffer.validate_transparent(), return);
3126            handle_potential_webgl_error!(self, self.validate_ownership(framebuffer), return);
3127            handle_object_deletion!(
3128                self,
3129                self.bound_draw_framebuffer,
3130                framebuffer,
3131                Some(WebGLCommand::BindFramebuffer(
3132                    framebuffer.target().unwrap(),
3133                    WebGLFramebufferBindingRequest::Default
3134                ))
3135            );
3136            framebuffer.delete(Operation::Infallible)
3137        }
3138    }
3139
3140    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
3141    fn DeleteRenderbuffer(&self, renderbuffer: Option<&WebGLRenderbuffer>) {
3142        if let Some(renderbuffer) = renderbuffer {
3143            handle_potential_webgl_error!(self, self.validate_ownership(renderbuffer), return);
3144            handle_object_deletion!(
3145                self,
3146                self.bound_renderbuffer,
3147                renderbuffer,
3148                Some(WebGLCommand::BindRenderbuffer(
3149                    constants::RENDERBUFFER,
3150                    None
3151                ))
3152            );
3153            renderbuffer.delete(Operation::Infallible)
3154        }
3155    }
3156
3157    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
3158    fn DeleteTexture(&self, texture: Option<&WebGLTexture>) {
3159        if let Some(texture) = texture {
3160            handle_potential_webgl_error!(self, self.validate_ownership(texture), return);
3161
3162            // From the GLES 2.0.25 spec, page 85:
3163            //
3164            //     "If a texture that is currently bound to one of the targets
3165            //      TEXTURE_2D, or TEXTURE_CUBE_MAP is deleted, it is as though
3166            //      BindTexture had been executed with the same target and texture
3167            //      zero."
3168            //
3169            // The same texture may be bound to multiple texture units.
3170            let mut active_unit_enum = self.textures.active_unit_enum();
3171            for (unit_enum, slot) in self.textures.iter() {
3172                if let Some(target) = slot.unbind(texture) {
3173                    if unit_enum != active_unit_enum {
3174                        self.send_command(WebGLCommand::ActiveTexture(unit_enum));
3175                        active_unit_enum = unit_enum;
3176                    }
3177                    self.send_command(WebGLCommand::BindTexture(target, None));
3178                }
3179            }
3180
3181            // Restore bound texture unit if it has been changed.
3182            if active_unit_enum != self.textures.active_unit_enum() {
3183                self.send_command(WebGLCommand::ActiveTexture(
3184                    self.textures.active_unit_enum(),
3185                ));
3186            }
3187
3188            texture.delete(Operation::Infallible)
3189        }
3190    }
3191
3192    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
3193    fn DeleteProgram(&self, program: Option<&WebGLProgram>) {
3194        if let Some(program) = program {
3195            handle_potential_webgl_error!(self, self.validate_ownership(program), return);
3196            program.mark_for_deletion(Operation::Infallible)
3197        }
3198    }
3199
3200    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
3201    fn DeleteShader(&self, shader: Option<&WebGLShader>) {
3202        if let Some(shader) = shader {
3203            handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
3204            shader.mark_for_deletion(Operation::Infallible)
3205        }
3206    }
3207
3208    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11>
3209    fn DrawArrays(&self, mode: u32, first: i32, count: i32) {
3210        handle_potential_webgl_error!(self, self.draw_arrays_instanced(mode, first, count, 1));
3211    }
3212
3213    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11>
3214    fn DrawElements(&self, mode: u32, count: i32, type_: u32, offset: i64) {
3215        handle_potential_webgl_error!(
3216            self,
3217            self.draw_elements_instanced(mode, count, type_, offset, 1)
3218        );
3219    }
3220
3221    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3222    fn EnableVertexAttribArray(&self, attrib_id: u32) {
3223        if attrib_id >= self.limits.max_vertex_attribs {
3224            return self.webgl_error(InvalidValue);
3225        }
3226        match self.webgl_version() {
3227            WebGLVersion::WebGL1 => self
3228                .current_vao()
3229                .enabled_vertex_attrib_array(attrib_id, true),
3230            WebGLVersion::WebGL2 => self
3231                .current_vao_webgl2()
3232                .enabled_vertex_attrib_array(attrib_id, true),
3233        };
3234        self.send_command(WebGLCommand::EnableVertexAttribArray(attrib_id));
3235    }
3236
3237    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3238    fn DisableVertexAttribArray(&self, attrib_id: u32) {
3239        if attrib_id >= self.limits.max_vertex_attribs {
3240            return self.webgl_error(InvalidValue);
3241        }
3242        match self.webgl_version() {
3243            WebGLVersion::WebGL1 => self
3244                .current_vao()
3245                .enabled_vertex_attrib_array(attrib_id, false),
3246            WebGLVersion::WebGL2 => self
3247                .current_vao_webgl2()
3248                .enabled_vertex_attrib_array(attrib_id, false),
3249        };
3250        self.send_command(WebGLCommand::DisableVertexAttribArray(attrib_id));
3251    }
3252
3253    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3254    fn GetActiveUniform(
3255        &self,
3256        program: &WebGLProgram,
3257        index: u32,
3258    ) -> Option<DomRoot<WebGLActiveInfo>> {
3259        handle_potential_webgl_error!(self, self.validate_ownership(program), return None);
3260        match program.get_active_uniform(index, CanGc::note()) {
3261            Ok(ret) => Some(ret),
3262            Err(e) => {
3263                self.webgl_error(e);
3264                None
3265            },
3266        }
3267    }
3268
3269    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3270    fn GetActiveAttrib(
3271        &self,
3272        program: &WebGLProgram,
3273        index: u32,
3274    ) -> Option<DomRoot<WebGLActiveInfo>> {
3275        handle_potential_webgl_error!(self, self.validate_ownership(program), return None);
3276        handle_potential_webgl_error!(
3277            self,
3278            program.get_active_attrib(index, CanGc::note()).map(Some),
3279            None
3280        )
3281    }
3282
3283    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3284    fn GetAttribLocation(&self, program: &WebGLProgram, name: DOMString) -> i32 {
3285        handle_potential_webgl_error!(self, self.validate_ownership(program), return -1);
3286        handle_potential_webgl_error!(self, program.get_attrib_location(name), -1)
3287    }
3288
3289    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
3290    fn GetFramebufferAttachmentParameter(
3291        &self,
3292        cx: SafeJSContext,
3293        target: u32,
3294        attachment: u32,
3295        pname: u32,
3296        mut retval: MutableHandleValue,
3297    ) {
3298        // Check if currently bound framebuffer is non-zero as per spec.
3299        if let Some(fb) = self.bound_draw_framebuffer.get() {
3300            // Opaque framebuffers cannot have their attachments inspected
3301            // https://immersive-web.github.io/webxr/#opaque-framebuffer
3302            handle_potential_webgl_error!(
3303                self,
3304                fb.validate_transparent(),
3305                return retval.set(NullValue())
3306            );
3307        } else {
3308            self.webgl_error(InvalidOperation);
3309            return retval.set(NullValue());
3310        }
3311
3312        // Note: commented out stuff is for the WebGL2 standard.
3313        let target_matches = match target {
3314            // constants::READ_FRAMEBUFFER |
3315            // constants::DRAW_FRAMEBUFFER => true,
3316            constants::FRAMEBUFFER => true,
3317            _ => false,
3318        };
3319        let attachment_matches = match attachment {
3320            // constants::MAX_COLOR_ATTACHMENTS ... gl::COLOR_ATTACHMENT0 |
3321            // constants::BACK |
3322            constants::COLOR_ATTACHMENT0 |
3323            constants::DEPTH_STENCIL_ATTACHMENT |
3324            constants::DEPTH_ATTACHMENT |
3325            constants::STENCIL_ATTACHMENT => true,
3326            _ => false,
3327        };
3328        let pname_matches = match pname {
3329            // constants::FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE |
3330            // constants::FRAMEBUFFER_ATTACHMENT_BLUE_SIZE |
3331            // constants::FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING |
3332            // constants::FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE |
3333            // constants::FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE |
3334            // constants::FRAMEBUFFER_ATTACHMENT_GREEN_SIZE |
3335            // constants::FRAMEBUFFER_ATTACHMENT_RED_SIZE |
3336            // constants::FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE |
3337            // constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER |
3338            constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME |
3339            constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE |
3340            constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE |
3341            constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL => true,
3342            _ => false,
3343        };
3344
3345        let bound_attachment_matches = match self
3346            .bound_draw_framebuffer
3347            .get()
3348            .unwrap()
3349            .attachment(attachment)
3350        {
3351            Some(attachment_root) => match attachment_root {
3352                WebGLFramebufferAttachmentRoot::Renderbuffer(_) => matches!(
3353                    pname,
3354                    constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE |
3355                        constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME
3356                ),
3357                WebGLFramebufferAttachmentRoot::Texture(_) => matches!(
3358                    pname,
3359                    constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE |
3360                        constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME |
3361                        constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL |
3362                        constants::FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE
3363                ),
3364            },
3365            _ => matches!(pname, constants::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE),
3366        };
3367
3368        if !target_matches || !attachment_matches || !pname_matches || !bound_attachment_matches {
3369            self.webgl_error(InvalidEnum);
3370            return retval.set(NullValue());
3371        }
3372
3373        // From the GLES2 spec:
3374        //
3375        //     If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE,
3376        //     then querying any other pname will generate INVALID_ENUM.
3377        //
3378        // otherwise, return `WebGLRenderbuffer` or `WebGLTexture` dom object
3379        if pname == constants::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME {
3380            // if fb is None, an INVALID_OPERATION is returned
3381            // at the beggining of the function, so `.unwrap()` will never panic
3382            let fb = self.bound_draw_framebuffer.get().unwrap();
3383            if let Some(webgl_attachment) = fb.attachment(attachment) {
3384                match webgl_attachment {
3385                    WebGLFramebufferAttachmentRoot::Renderbuffer(rb) => {
3386                        rb.safe_to_jsval(cx, retval, CanGc::note());
3387                        return;
3388                    },
3389                    WebGLFramebufferAttachmentRoot::Texture(texture) => {
3390                        texture.safe_to_jsval(cx, retval, CanGc::note());
3391                        return;
3392                    },
3393                }
3394            }
3395            self.webgl_error(InvalidEnum);
3396            return retval.set(NullValue());
3397        }
3398
3399        let (sender, receiver) = webgl_channel().unwrap();
3400        self.send_command(WebGLCommand::GetFramebufferAttachmentParameter(
3401            target, attachment, pname, sender,
3402        ));
3403
3404        retval.set(Int32Value(receiver.recv().unwrap()))
3405    }
3406
3407    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
3408    fn GetRenderbufferParameter(
3409        &self,
3410        _cx: SafeJSContext,
3411        target: u32,
3412        pname: u32,
3413        mut retval: MutableHandleValue,
3414    ) {
3415        // We do not check to see if the renderbuffer came from an opaque framebuffer
3416        // https://github.com/immersive-web/webxr/issues/862
3417        let target_matches = target == constants::RENDERBUFFER;
3418
3419        let pname_matches = matches!(
3420            pname,
3421            constants::RENDERBUFFER_WIDTH |
3422                constants::RENDERBUFFER_HEIGHT |
3423                constants::RENDERBUFFER_INTERNAL_FORMAT |
3424                constants::RENDERBUFFER_RED_SIZE |
3425                constants::RENDERBUFFER_GREEN_SIZE |
3426                constants::RENDERBUFFER_BLUE_SIZE |
3427                constants::RENDERBUFFER_ALPHA_SIZE |
3428                constants::RENDERBUFFER_DEPTH_SIZE |
3429                constants::RENDERBUFFER_STENCIL_SIZE
3430        );
3431
3432        if !target_matches || !pname_matches {
3433            self.webgl_error(InvalidEnum);
3434            return retval.set(NullValue());
3435        }
3436
3437        if self.bound_renderbuffer.get().is_none() {
3438            self.webgl_error(InvalidOperation);
3439            return retval.set(NullValue());
3440        }
3441
3442        let result = if pname == constants::RENDERBUFFER_INTERNAL_FORMAT {
3443            let rb = self.bound_renderbuffer.get().unwrap();
3444            rb.internal_format() as i32
3445        } else {
3446            let (sender, receiver) = webgl_channel().unwrap();
3447            self.send_command(WebGLCommand::GetRenderbufferParameter(
3448                target, pname, sender,
3449            ));
3450            receiver.recv().unwrap()
3451        };
3452
3453        retval.set(Int32Value(result))
3454    }
3455
3456    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
3457    fn GetProgramInfoLog(&self, program: &WebGLProgram) -> Option<DOMString> {
3458        handle_potential_webgl_error!(self, self.validate_ownership(program), return None);
3459        match program.get_info_log() {
3460            Ok(value) => Some(DOMString::from(value)),
3461            Err(e) => {
3462                self.webgl_error(e);
3463                None
3464            },
3465        }
3466    }
3467
3468    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
3469    fn GetProgramParameter(
3470        &self,
3471        _: SafeJSContext,
3472        program: &WebGLProgram,
3473        param: u32,
3474        mut retval: MutableHandleValue,
3475    ) {
3476        handle_potential_webgl_error!(
3477            self,
3478            self.validate_ownership(program),
3479            return retval.set(NullValue())
3480        );
3481        if program.is_deleted() {
3482            self.webgl_error(InvalidOperation);
3483            return retval.set(NullValue());
3484        }
3485        retval.set(match param {
3486            constants::DELETE_STATUS => BooleanValue(program.is_marked_for_deletion()),
3487            constants::LINK_STATUS => BooleanValue(program.is_linked()),
3488            constants::VALIDATE_STATUS => {
3489                // FIXME(nox): This could be cached on the DOM side when we call validateProgram
3490                // but I'm not sure when the value should be reset.
3491                let (sender, receiver) = webgl_channel().unwrap();
3492                self.send_command(WebGLCommand::GetProgramValidateStatus(program.id(), sender));
3493                BooleanValue(receiver.recv().unwrap())
3494            },
3495            constants::ATTACHED_SHADERS => {
3496                // FIXME(nox): This allocates a vector and roots a couple of shaders for nothing.
3497                Int32Value(
3498                    program
3499                        .attached_shaders()
3500                        .map(|shaders| shaders.len() as i32)
3501                        .unwrap_or(0),
3502                )
3503            },
3504            constants::ACTIVE_ATTRIBUTES => Int32Value(program.active_attribs().len() as i32),
3505            constants::ACTIVE_UNIFORMS => Int32Value(program.active_uniforms().len() as i32),
3506            _ => {
3507                self.webgl_error(InvalidEnum);
3508                NullValue()
3509            },
3510        })
3511    }
3512
3513    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
3514    fn GetShaderInfoLog(&self, shader: &WebGLShader) -> Option<DOMString> {
3515        handle_potential_webgl_error!(self, self.validate_ownership(shader), return None);
3516        Some(shader.info_log())
3517    }
3518
3519    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
3520    fn GetShaderParameter(
3521        &self,
3522        _: SafeJSContext,
3523        shader: &WebGLShader,
3524        param: u32,
3525        mut retval: MutableHandleValue,
3526    ) {
3527        handle_potential_webgl_error!(
3528            self,
3529            self.validate_ownership(shader),
3530            return retval.set(NullValue())
3531        );
3532        if shader.is_deleted() {
3533            self.webgl_error(InvalidValue);
3534            return retval.set(NullValue());
3535        }
3536        retval.set(match param {
3537            constants::DELETE_STATUS => BooleanValue(shader.is_marked_for_deletion()),
3538            constants::COMPILE_STATUS => BooleanValue(shader.successfully_compiled()),
3539            constants::SHADER_TYPE => UInt32Value(shader.gl_type()),
3540            _ => {
3541                self.webgl_error(InvalidEnum);
3542                NullValue()
3543            },
3544        })
3545    }
3546
3547    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
3548    fn GetShaderPrecisionFormat(
3549        &self,
3550        shader_type: u32,
3551        precision_type: u32,
3552    ) -> Option<DomRoot<WebGLShaderPrecisionFormat>> {
3553        match shader_type {
3554            constants::FRAGMENT_SHADER | constants::VERTEX_SHADER => (),
3555            _ => {
3556                self.webgl_error(InvalidEnum);
3557                return None;
3558            },
3559        }
3560
3561        match precision_type {
3562            constants::LOW_FLOAT |
3563            constants::MEDIUM_FLOAT |
3564            constants::HIGH_FLOAT |
3565            constants::LOW_INT |
3566            constants::MEDIUM_INT |
3567            constants::HIGH_INT => (),
3568            _ => {
3569                self.webgl_error(InvalidEnum);
3570                return None;
3571            },
3572        }
3573
3574        let (sender, receiver) = webgl_channel().unwrap();
3575        self.send_command(WebGLCommand::GetShaderPrecisionFormat(
3576            shader_type,
3577            precision_type,
3578            sender,
3579        ));
3580
3581        let (range_min, range_max, precision) = receiver.recv().unwrap();
3582        Some(WebGLShaderPrecisionFormat::new(
3583            self.global().as_window(),
3584            range_min,
3585            range_max,
3586            precision,
3587            CanGc::note(),
3588        ))
3589    }
3590
3591    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3592    fn GetUniformLocation(
3593        &self,
3594        program: &WebGLProgram,
3595        name: DOMString,
3596    ) -> Option<DomRoot<WebGLUniformLocation>> {
3597        handle_potential_webgl_error!(self, self.validate_ownership(program), return None);
3598        handle_potential_webgl_error!(
3599            self,
3600            program.get_uniform_location(name, CanGc::note()),
3601            None
3602        )
3603    }
3604
3605    #[allow(unsafe_code)]
3606    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
3607    fn GetVertexAttrib(
3608        &self,
3609        cx: SafeJSContext,
3610        index: u32,
3611        param: u32,
3612        mut retval: MutableHandleValue,
3613    ) {
3614        let mut get_attrib = |data: Ref<'_, VertexAttribData>| {
3615            if param == constants::CURRENT_VERTEX_ATTRIB {
3616                let attrib = self.current_vertex_attribs.borrow()[index as usize];
3617                match attrib {
3618                    VertexAttrib::Float(x, y, z, w) => {
3619                        let value = [x, y, z, w];
3620                        unsafe {
3621                            rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>());
3622                            Float32Array::create(
3623                                *cx,
3624                                CreateWith::Slice(&value),
3625                                result.handle_mut(),
3626                            )
3627                            .unwrap();
3628                            return retval.set(ObjectValue(result.get()));
3629                        }
3630                    },
3631                    VertexAttrib::Int(x, y, z, w) => {
3632                        let value = [x, y, z, w];
3633                        unsafe {
3634                            rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>());
3635                            Int32Array::create(*cx, CreateWith::Slice(&value), result.handle_mut())
3636                                .unwrap();
3637                            return retval.set(ObjectValue(result.get()));
3638                        }
3639                    },
3640                    VertexAttrib::Uint(x, y, z, w) => {
3641                        let value = [x, y, z, w];
3642                        unsafe {
3643                            rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>());
3644                            Uint32Array::create(
3645                                *cx,
3646                                CreateWith::Slice(&value),
3647                                result.handle_mut(),
3648                            )
3649                            .unwrap();
3650                            return retval.set(ObjectValue(result.get()));
3651                        }
3652                    },
3653                };
3654            }
3655            if !self
3656                .extension_manager
3657                .is_get_vertex_attrib_name_enabled(param)
3658            {
3659                self.webgl_error(WebGLError::InvalidEnum);
3660                return retval.set(NullValue());
3661            }
3662
3663            match param {
3664                constants::VERTEX_ATTRIB_ARRAY_ENABLED => {
3665                    retval.set(BooleanValue(data.enabled_as_array))
3666                },
3667                constants::VERTEX_ATTRIB_ARRAY_SIZE => retval.set(Int32Value(data.size as i32)),
3668                constants::VERTEX_ATTRIB_ARRAY_TYPE => retval.set(Int32Value(data.type_ as i32)),
3669                constants::VERTEX_ATTRIB_ARRAY_NORMALIZED => {
3670                    retval.set(BooleanValue(data.normalized))
3671                },
3672                constants::VERTEX_ATTRIB_ARRAY_STRIDE => retval.set(Int32Value(data.stride as i32)),
3673                constants::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING => {
3674                    if let Some(buffer) = data.buffer() {
3675                        buffer.safe_to_jsval(cx, retval.reborrow(), CanGc::note());
3676                    } else {
3677                        retval.set(NullValue());
3678                    }
3679                },
3680                ANGLEInstancedArraysConstants::VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE => {
3681                    retval.set(UInt32Value(data.divisor))
3682                },
3683                _ => {
3684                    self.webgl_error(InvalidEnum);
3685                    retval.set(NullValue())
3686                },
3687            }
3688        };
3689
3690        match self.webgl_version() {
3691            WebGLVersion::WebGL1 => {
3692                let current_vao = self.current_vao();
3693                let data = handle_potential_webgl_error!(
3694                    self,
3695                    current_vao.get_vertex_attrib(index).ok_or(InvalidValue),
3696                    return retval.set(NullValue())
3697                );
3698                get_attrib(data)
3699            },
3700            WebGLVersion::WebGL2 => {
3701                let current_vao = self.current_vao_webgl2();
3702                let data = handle_potential_webgl_error!(
3703                    self,
3704                    current_vao.get_vertex_attrib(index).ok_or(InvalidValue),
3705                    return retval.set(NullValue())
3706                );
3707                get_attrib(data)
3708            },
3709        }
3710    }
3711
3712    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
3713    fn GetVertexAttribOffset(&self, index: u32, pname: u32) -> i64 {
3714        if pname != constants::VERTEX_ATTRIB_ARRAY_POINTER {
3715            self.webgl_error(InvalidEnum);
3716            return 0;
3717        }
3718        match self.webgl_version() {
3719            WebGLVersion::WebGL1 => {
3720                let current_vao = self.current_vao();
3721                let data = handle_potential_webgl_error!(
3722                    self,
3723                    current_vao.get_vertex_attrib(index).ok_or(InvalidValue),
3724                    return 0
3725                );
3726                data.offset as i64
3727            },
3728            WebGLVersion::WebGL2 => {
3729                let current_vao = self.current_vao_webgl2();
3730                let data = handle_potential_webgl_error!(
3731                    self,
3732                    current_vao.get_vertex_attrib(index).ok_or(InvalidValue),
3733                    return 0
3734                );
3735                data.offset as i64
3736            },
3737        }
3738    }
3739
3740    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
3741    fn Hint(&self, target: u32, mode: u32) {
3742        if target != constants::GENERATE_MIPMAP_HINT &&
3743            !self.extension_manager.is_hint_target_enabled(target)
3744        {
3745            return self.webgl_error(InvalidEnum);
3746        }
3747
3748        match mode {
3749            constants::FASTEST | constants::NICEST | constants::DONT_CARE => (),
3750
3751            _ => return self.webgl_error(InvalidEnum),
3752        }
3753
3754        self.send_command(WebGLCommand::Hint(target, mode));
3755    }
3756
3757    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5>
3758    fn IsBuffer(&self, buffer: Option<&WebGLBuffer>) -> bool {
3759        buffer.is_some_and(|buf| {
3760            self.validate_ownership(buf).is_ok() && buf.target().is_some() && !buf.is_deleted()
3761        })
3762    }
3763
3764    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
3765    fn IsEnabled(&self, cap: u32) -> bool {
3766        handle_potential_webgl_error!(self, self.capabilities.is_enabled(cap), false)
3767    }
3768
3769    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
3770    fn IsFramebuffer(&self, frame_buffer: Option<&WebGLFramebuffer>) -> bool {
3771        frame_buffer.is_some_and(|buf| {
3772            self.validate_ownership(buf).is_ok() && buf.target().is_some() && !buf.is_deleted()
3773        })
3774    }
3775
3776    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
3777    fn IsProgram(&self, program: Option<&WebGLProgram>) -> bool {
3778        program.is_some_and(|p| self.validate_ownership(p).is_ok() && !p.is_deleted())
3779    }
3780
3781    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
3782    fn IsRenderbuffer(&self, render_buffer: Option<&WebGLRenderbuffer>) -> bool {
3783        render_buffer.is_some_and(|buf| {
3784            self.validate_ownership(buf).is_ok() && buf.ever_bound() && !buf.is_deleted()
3785        })
3786    }
3787
3788    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
3789    fn IsShader(&self, shader: Option<&WebGLShader>) -> bool {
3790        shader.is_some_and(|s| self.validate_ownership(s).is_ok() && !s.is_deleted())
3791    }
3792
3793    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
3794    fn IsTexture(&self, texture: Option<&WebGLTexture>) -> bool {
3795        texture.is_some_and(|tex| {
3796            self.validate_ownership(tex).is_ok() && tex.target().is_some() && !tex.is_invalid()
3797        })
3798    }
3799
3800    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
3801    fn LineWidth(&self, width: f32) {
3802        if width.is_nan() || width <= 0f32 {
3803            return self.webgl_error(InvalidValue);
3804        }
3805
3806        self.send_command(WebGLCommand::LineWidth(width))
3807    }
3808
3809    /// NOTE: Usage of this function could affect rendering while we keep using
3810    ///   readback to render to the page.
3811    ///
3812    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
3813    fn PixelStorei(&self, param_name: u32, param_value: i32) {
3814        let mut texture_settings = self.texture_unpacking_settings.get();
3815        match param_name {
3816            constants::UNPACK_FLIP_Y_WEBGL => {
3817                texture_settings.set(TextureUnpacking::FLIP_Y_AXIS, param_value != 0);
3818            },
3819            constants::UNPACK_PREMULTIPLY_ALPHA_WEBGL => {
3820                texture_settings.set(TextureUnpacking::PREMULTIPLY_ALPHA, param_value != 0);
3821            },
3822            constants::UNPACK_COLORSPACE_CONVERSION_WEBGL => {
3823                let convert = match param_value as u32 {
3824                    constants::BROWSER_DEFAULT_WEBGL => true,
3825                    constants::NONE => false,
3826                    _ => return self.webgl_error(InvalidEnum),
3827                };
3828                texture_settings.set(TextureUnpacking::CONVERT_COLORSPACE, convert);
3829            },
3830            constants::UNPACK_ALIGNMENT => {
3831                match param_value {
3832                    1 | 2 | 4 | 8 => (),
3833                    _ => return self.webgl_error(InvalidValue),
3834                }
3835                self.texture_unpacking_alignment.set(param_value as u32);
3836                return;
3837            },
3838            constants::PACK_ALIGNMENT => {
3839                match param_value {
3840                    1 | 2 | 4 | 8 => (),
3841                    _ => return self.webgl_error(InvalidValue),
3842                }
3843                // We never actually change the actual value on the GL side
3844                // because it's better to receive the pixels without the padding
3845                // and then write the result at the right place in ReadPixels.
3846                self.texture_packing_alignment.set(param_value as u8);
3847                return;
3848            },
3849            _ => return self.webgl_error(InvalidEnum),
3850        }
3851        self.texture_unpacking_settings.set(texture_settings);
3852    }
3853
3854    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
3855    fn PolygonOffset(&self, factor: f32, units: f32) {
3856        self.send_command(WebGLCommand::PolygonOffset(factor, units))
3857    }
3858
3859    // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.12
3860    #[allow(unsafe_code)]
3861    fn ReadPixels(
3862        &self,
3863        x: i32,
3864        y: i32,
3865        width: i32,
3866        height: i32,
3867        format: u32,
3868        pixel_type: u32,
3869        mut pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
3870    ) {
3871        handle_potential_webgl_error!(self, self.validate_framebuffer(), return);
3872
3873        let pixels =
3874            handle_potential_webgl_error!(self, pixels.as_mut().ok_or(InvalidValue), return);
3875
3876        if width < 0 || height < 0 {
3877            return self.webgl_error(InvalidValue);
3878        }
3879
3880        if format != constants::RGBA || pixel_type != constants::UNSIGNED_BYTE {
3881            return self.webgl_error(InvalidOperation);
3882        }
3883
3884        if pixels.get_array_type() != Type::Uint8 {
3885            return self.webgl_error(InvalidOperation);
3886        }
3887
3888        let (fb_width, fb_height) = handle_potential_webgl_error!(
3889            self,
3890            self.get_current_framebuffer_size().ok_or(InvalidOperation),
3891            return
3892        );
3893
3894        if width == 0 || height == 0 {
3895            return;
3896        }
3897
3898        let bytes_per_pixel = 4;
3899
3900        let row_len = handle_potential_webgl_error!(
3901            self,
3902            width.checked_mul(bytes_per_pixel).ok_or(InvalidOperation),
3903            return
3904        );
3905
3906        let pack_alignment = self.texture_packing_alignment.get() as i32;
3907        let dest_padding = match row_len % pack_alignment {
3908            0 => 0,
3909            remainder => pack_alignment - remainder,
3910        };
3911        let dest_stride = row_len + dest_padding;
3912
3913        let full_rows_len = handle_potential_webgl_error!(
3914            self,
3915            dest_stride.checked_mul(height - 1).ok_or(InvalidOperation),
3916            return
3917        );
3918        let required_dest_len = handle_potential_webgl_error!(
3919            self,
3920            full_rows_len.checked_add(row_len).ok_or(InvalidOperation),
3921            return
3922        );
3923
3924        let dest = unsafe { pixels.as_mut_slice() };
3925        if dest.len() < required_dest_len as usize {
3926            return self.webgl_error(InvalidOperation);
3927        }
3928
3929        let src_origin = Point2D::new(x, y);
3930        let src_size = Size2D::new(width as u32, height as u32);
3931        let fb_size = Size2D::new(fb_width as u32, fb_height as u32);
3932        let src_rect = match pixels::clip(src_origin, src_size.to_u32(), fb_size.to_u32()) {
3933            Some(rect) => rect,
3934            None => return,
3935        };
3936
3937        // Note: we're casting a Rect<u64> back into a Rect<u32> here, but it's okay because
3938        //  it used u32 data types to begin with. It just got converted to Rect<u64> in
3939        //  pixels::clip
3940        let src_rect = src_rect.to_u32();
3941
3942        let mut dest_offset = 0;
3943        if x < 0 {
3944            dest_offset += -x * bytes_per_pixel;
3945        }
3946        if y < 0 {
3947            dest_offset += -y * row_len;
3948        }
3949
3950        let (sender, receiver) = ipc::channel().unwrap();
3951        self.send_command(WebGLCommand::ReadPixels(
3952            src_rect, format, pixel_type, sender,
3953        ));
3954        let (src, _) = receiver.recv().unwrap();
3955
3956        let src_row_len = src_rect.size.width as usize * bytes_per_pixel as usize;
3957        for i in 0..src_rect.size.height {
3958            let dest_start = dest_offset as usize + i as usize * dest_stride as usize;
3959            let dest_end = dest_start + src_row_len;
3960            let src_start = i as usize * src_row_len;
3961            let src_end = src_start + src_row_len;
3962            dest[dest_start..dest_end].copy_from_slice(&src[src_start..src_end]);
3963        }
3964    }
3965
3966    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
3967    fn SampleCoverage(&self, value: f32, invert: bool) {
3968        self.send_command(WebGLCommand::SampleCoverage(value, invert));
3969    }
3970
3971    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4>
3972    fn Scissor(&self, x: i32, y: i32, width: i32, height: i32) {
3973        if width < 0 || height < 0 {
3974            return self.webgl_error(InvalidValue);
3975        }
3976
3977        let width = width as u32;
3978        let height = height as u32;
3979
3980        self.current_scissor.set((x, y, width, height));
3981        self.send_command(WebGLCommand::Scissor(x, y, width, height));
3982    }
3983
3984    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
3985    fn StencilFunc(&self, func: u32, ref_: i32, mask: u32) {
3986        match func {
3987            constants::NEVER |
3988            constants::LESS |
3989            constants::EQUAL |
3990            constants::LEQUAL |
3991            constants::GREATER |
3992            constants::NOTEQUAL |
3993            constants::GEQUAL |
3994            constants::ALWAYS => self.send_command(WebGLCommand::StencilFunc(func, ref_, mask)),
3995            _ => self.webgl_error(InvalidEnum),
3996        }
3997    }
3998
3999    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
4000    fn StencilFuncSeparate(&self, face: u32, func: u32, ref_: i32, mask: u32) {
4001        match face {
4002            constants::FRONT | constants::BACK | constants::FRONT_AND_BACK => (),
4003            _ => return self.webgl_error(InvalidEnum),
4004        }
4005
4006        match func {
4007            constants::NEVER |
4008            constants::LESS |
4009            constants::EQUAL |
4010            constants::LEQUAL |
4011            constants::GREATER |
4012            constants::NOTEQUAL |
4013            constants::GEQUAL |
4014            constants::ALWAYS => {
4015                self.send_command(WebGLCommand::StencilFuncSeparate(face, func, ref_, mask))
4016            },
4017            _ => self.webgl_error(InvalidEnum),
4018        }
4019    }
4020
4021    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
4022    fn StencilMask(&self, mask: u32) {
4023        self.send_command(WebGLCommand::StencilMask(mask))
4024    }
4025
4026    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
4027    fn StencilMaskSeparate(&self, face: u32, mask: u32) {
4028        match face {
4029            constants::FRONT | constants::BACK | constants::FRONT_AND_BACK => {
4030                self.send_command(WebGLCommand::StencilMaskSeparate(face, mask))
4031            },
4032            _ => self.webgl_error(InvalidEnum),
4033        };
4034    }
4035
4036    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
4037    fn StencilOp(&self, fail: u32, zfail: u32, zpass: u32) {
4038        if self.validate_stencil_actions(fail) &&
4039            self.validate_stencil_actions(zfail) &&
4040            self.validate_stencil_actions(zpass)
4041        {
4042            self.send_command(WebGLCommand::StencilOp(fail, zfail, zpass));
4043        } else {
4044            self.webgl_error(InvalidEnum)
4045        }
4046    }
4047
4048    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3>
4049    fn StencilOpSeparate(&self, face: u32, fail: u32, zfail: u32, zpass: u32) {
4050        match face {
4051            constants::FRONT | constants::BACK | constants::FRONT_AND_BACK => (),
4052            _ => return self.webgl_error(InvalidEnum),
4053        }
4054
4055        if self.validate_stencil_actions(fail) &&
4056            self.validate_stencil_actions(zfail) &&
4057            self.validate_stencil_actions(zpass)
4058        {
4059            self.send_command(WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass))
4060        } else {
4061            self.webgl_error(InvalidEnum)
4062        }
4063    }
4064
4065    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
4066    fn LinkProgram(&self, program: &WebGLProgram) {
4067        handle_potential_webgl_error!(self, self.validate_ownership(program), return);
4068        if program.is_deleted() {
4069            return self.webgl_error(InvalidValue);
4070        }
4071        handle_potential_webgl_error!(self, program.link());
4072    }
4073
4074    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
4075    fn ShaderSource(&self, shader: &WebGLShader, source: DOMString) {
4076        handle_potential_webgl_error!(self, self.validate_ownership(shader), return);
4077        shader.set_source(source)
4078    }
4079
4080    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
4081    fn GetShaderSource(&self, shader: &WebGLShader) -> Option<DOMString> {
4082        handle_potential_webgl_error!(self, self.validate_ownership(shader), return None);
4083        Some(shader.source())
4084    }
4085
4086    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4087    fn Uniform1f(&self, location: Option<&WebGLUniformLocation>, val: f32) {
4088        self.with_location(location, |location| {
4089            match location.type_() {
4090                constants::BOOL | constants::FLOAT => {},
4091                _ => return Err(InvalidOperation),
4092            }
4093            self.send_command(WebGLCommand::Uniform1f(location.id(), val));
4094            Ok(())
4095        });
4096    }
4097
4098    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4099    fn Uniform1i(&self, location: Option<&WebGLUniformLocation>, val: i32) {
4100        self.with_location(location, |location| {
4101            match location.type_() {
4102                constants::BOOL | constants::INT => {},
4103                constants::SAMPLER_2D |
4104                WebGL2RenderingContextConstants::SAMPLER_3D |
4105                WebGL2RenderingContextConstants::SAMPLER_2D_ARRAY |
4106                constants::SAMPLER_CUBE => {
4107                    if val < 0 || val as u32 >= self.limits.max_combined_texture_image_units {
4108                        return Err(InvalidValue);
4109                    }
4110                },
4111                _ => return Err(InvalidOperation),
4112            }
4113            self.send_command(WebGLCommand::Uniform1i(location.id(), val));
4114            Ok(())
4115        });
4116    }
4117
4118    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4119    fn Uniform1iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) {
4120        self.uniform1iv(location, val, 0, 0)
4121    }
4122
4123    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4124    fn Uniform1fv(
4125        &self,
4126        location: Option<&WebGLUniformLocation>,
4127        val: Float32ArrayOrUnrestrictedFloatSequence,
4128    ) {
4129        self.uniform1fv(location, val, 0, 0)
4130    }
4131
4132    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4133    fn Uniform2f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32) {
4134        self.with_location(location, |location| {
4135            match location.type_() {
4136                constants::BOOL_VEC2 | constants::FLOAT_VEC2 => {},
4137                _ => return Err(InvalidOperation),
4138            }
4139            self.send_command(WebGLCommand::Uniform2f(location.id(), x, y));
4140            Ok(())
4141        });
4142    }
4143
4144    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4145    fn Uniform2fv(
4146        &self,
4147        location: Option<&WebGLUniformLocation>,
4148        val: Float32ArrayOrUnrestrictedFloatSequence,
4149    ) {
4150        self.uniform2fv(location, val, 0, 0)
4151    }
4152
4153    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4154    fn Uniform2i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32) {
4155        self.with_location(location, |location| {
4156            match location.type_() {
4157                constants::BOOL_VEC2 | constants::INT_VEC2 => {},
4158                _ => return Err(InvalidOperation),
4159            }
4160            self.send_command(WebGLCommand::Uniform2i(location.id(), x, y));
4161            Ok(())
4162        });
4163    }
4164
4165    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4166    fn Uniform2iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) {
4167        self.uniform2iv(location, val, 0, 0)
4168    }
4169
4170    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4171    fn Uniform3f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32) {
4172        self.with_location(location, |location| {
4173            match location.type_() {
4174                constants::BOOL_VEC3 | constants::FLOAT_VEC3 => {},
4175                _ => return Err(InvalidOperation),
4176            }
4177            self.send_command(WebGLCommand::Uniform3f(location.id(), x, y, z));
4178            Ok(())
4179        });
4180    }
4181
4182    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4183    fn Uniform3fv(
4184        &self,
4185        location: Option<&WebGLUniformLocation>,
4186        val: Float32ArrayOrUnrestrictedFloatSequence,
4187    ) {
4188        self.uniform3fv(location, val, 0, 0)
4189    }
4190
4191    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4192    fn Uniform3i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32) {
4193        self.with_location(location, |location| {
4194            match location.type_() {
4195                constants::BOOL_VEC3 | constants::INT_VEC3 => {},
4196                _ => return Err(InvalidOperation),
4197            }
4198            self.send_command(WebGLCommand::Uniform3i(location.id(), x, y, z));
4199            Ok(())
4200        });
4201    }
4202
4203    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4204    fn Uniform3iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) {
4205        self.uniform3iv(location, val, 0, 0)
4206    }
4207
4208    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4209    fn Uniform4i(&self, location: Option<&WebGLUniformLocation>, x: i32, y: i32, z: i32, w: i32) {
4210        self.with_location(location, |location| {
4211            match location.type_() {
4212                constants::BOOL_VEC4 | constants::INT_VEC4 => {},
4213                _ => return Err(InvalidOperation),
4214            }
4215            self.send_command(WebGLCommand::Uniform4i(location.id(), x, y, z, w));
4216            Ok(())
4217        });
4218    }
4219
4220    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4221    fn Uniform4iv(&self, location: Option<&WebGLUniformLocation>, val: Int32ArrayOrLongSequence) {
4222        self.uniform4iv(location, val, 0, 0)
4223    }
4224
4225    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4226    fn Uniform4f(&self, location: Option<&WebGLUniformLocation>, x: f32, y: f32, z: f32, w: f32) {
4227        self.with_location(location, |location| {
4228            match location.type_() {
4229                constants::BOOL_VEC4 | constants::FLOAT_VEC4 => {},
4230                _ => return Err(InvalidOperation),
4231            }
4232            self.send_command(WebGLCommand::Uniform4f(location.id(), x, y, z, w));
4233            Ok(())
4234        });
4235    }
4236
4237    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4238    fn Uniform4fv(
4239        &self,
4240        location: Option<&WebGLUniformLocation>,
4241        val: Float32ArrayOrUnrestrictedFloatSequence,
4242    ) {
4243        self.uniform4fv(location, val, 0, 0)
4244    }
4245
4246    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4247    fn UniformMatrix2fv(
4248        &self,
4249        location: Option<&WebGLUniformLocation>,
4250        transpose: bool,
4251        val: Float32ArrayOrUnrestrictedFloatSequence,
4252    ) {
4253        self.uniform_matrix_2fv(location, transpose, val, 0, 0)
4254    }
4255
4256    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4257    fn UniformMatrix3fv(
4258        &self,
4259        location: Option<&WebGLUniformLocation>,
4260        transpose: bool,
4261        val: Float32ArrayOrUnrestrictedFloatSequence,
4262    ) {
4263        self.uniform_matrix_3fv(location, transpose, val, 0, 0)
4264    }
4265
4266    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4267    fn UniformMatrix4fv(
4268        &self,
4269        location: Option<&WebGLUniformLocation>,
4270        transpose: bool,
4271        val: Float32ArrayOrUnrestrictedFloatSequence,
4272    ) {
4273        self.uniform_matrix_4fv(location, transpose, val, 0, 0)
4274    }
4275
4276    // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
4277    #[allow(unsafe_code)]
4278    fn GetUniform(
4279        &self,
4280        cx: SafeJSContext,
4281        program: &WebGLProgram,
4282        location: &WebGLUniformLocation,
4283        mut rval: MutableHandleValue,
4284    ) {
4285        handle_potential_webgl_error!(
4286            self,
4287            self.uniform_check_program(program, location),
4288            return rval.set(NullValue())
4289        );
4290
4291        let triple = (self, program.id(), location.id());
4292
4293        match location.type_() {
4294            constants::BOOL => rval.set(BooleanValue(uniform_get(
4295                triple,
4296                WebGLCommand::GetUniformBool,
4297            ))),
4298            constants::BOOL_VEC2 => uniform_get(triple, WebGLCommand::GetUniformBool2)
4299                .safe_to_jsval(cx, rval, CanGc::note()),
4300            constants::BOOL_VEC3 => uniform_get(triple, WebGLCommand::GetUniformBool3)
4301                .safe_to_jsval(cx, rval, CanGc::note()),
4302            constants::BOOL_VEC4 => uniform_get(triple, WebGLCommand::GetUniformBool4)
4303                .safe_to_jsval(cx, rval, CanGc::note()),
4304            constants::INT |
4305            constants::SAMPLER_2D |
4306            constants::SAMPLER_CUBE |
4307            WebGL2RenderingContextConstants::SAMPLER_2D_ARRAY |
4308            WebGL2RenderingContextConstants::SAMPLER_3D => {
4309                rval.set(Int32Value(uniform_get(triple, WebGLCommand::GetUniformInt)))
4310            },
4311            constants::INT_VEC2 => unsafe {
4312                uniform_typed::<Int32>(
4313                    *cx,
4314                    &uniform_get(triple, WebGLCommand::GetUniformInt2),
4315                    rval,
4316                )
4317            },
4318            constants::INT_VEC3 => unsafe {
4319                uniform_typed::<Int32>(
4320                    *cx,
4321                    &uniform_get(triple, WebGLCommand::GetUniformInt3),
4322                    rval,
4323                )
4324            },
4325            constants::INT_VEC4 => unsafe {
4326                uniform_typed::<Int32>(
4327                    *cx,
4328                    &uniform_get(triple, WebGLCommand::GetUniformInt4),
4329                    rval,
4330                )
4331            },
4332            constants::FLOAT => rval
4333                .set(DoubleValue(
4334                    uniform_get(triple, WebGLCommand::GetUniformFloat) as f64,
4335                )),
4336            constants::FLOAT_VEC2 => unsafe {
4337                uniform_typed::<Float32>(
4338                    *cx,
4339                    &uniform_get(triple, WebGLCommand::GetUniformFloat2),
4340                    rval,
4341                )
4342            },
4343            constants::FLOAT_VEC3 => unsafe {
4344                uniform_typed::<Float32>(
4345                    *cx,
4346                    &uniform_get(triple, WebGLCommand::GetUniformFloat3),
4347                    rval,
4348                )
4349            },
4350            constants::FLOAT_VEC4 | constants::FLOAT_MAT2 => unsafe {
4351                uniform_typed::<Float32>(
4352                    *cx,
4353                    &uniform_get(triple, WebGLCommand::GetUniformFloat4),
4354                    rval,
4355                )
4356            },
4357            constants::FLOAT_MAT3 => unsafe {
4358                uniform_typed::<Float32>(
4359                    *cx,
4360                    &uniform_get(triple, WebGLCommand::GetUniformFloat9),
4361                    rval,
4362                )
4363            },
4364            constants::FLOAT_MAT4 => unsafe {
4365                uniform_typed::<Float32>(
4366                    *cx,
4367                    &uniform_get(triple, WebGLCommand::GetUniformFloat16),
4368                    rval,
4369                )
4370            },
4371            _ => panic!("wrong uniform type"),
4372        }
4373    }
4374
4375    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
4376    fn UseProgram(&self, program: Option<&WebGLProgram>) {
4377        if let Some(program) = program {
4378            handle_potential_webgl_error!(self, self.validate_ownership(program), return);
4379            if program.is_deleted() || !program.is_linked() {
4380                return self.webgl_error(InvalidOperation);
4381            }
4382            if program.is_in_use() {
4383                return;
4384            }
4385            program.in_use(true);
4386        }
4387        match self.current_program.get() {
4388            Some(ref current) if program != Some(&**current) => current.in_use(false),
4389            _ => {},
4390        }
4391        self.send_command(WebGLCommand::UseProgram(program.map(|p| p.id())));
4392        self.current_program.set(program);
4393    }
4394
4395    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
4396    fn ValidateProgram(&self, program: &WebGLProgram) {
4397        handle_potential_webgl_error!(self, self.validate_ownership(program), return);
4398        if let Err(e) = program.validate() {
4399            self.webgl_error(e);
4400        }
4401    }
4402
4403    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4404    fn VertexAttrib1f(&self, indx: u32, x: f32) {
4405        self.vertex_attrib(indx, x, 0f32, 0f32, 1f32)
4406    }
4407
4408    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4409    fn VertexAttrib1fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
4410        let values = match v {
4411            Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
4412            Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
4413        };
4414        if values.is_empty() {
4415            // https://github.com/KhronosGroup/WebGL/issues/2700
4416            return self.webgl_error(InvalidValue);
4417        }
4418        self.vertex_attrib(indx, values[0], 0f32, 0f32, 1f32);
4419    }
4420
4421    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4422    fn VertexAttrib2f(&self, indx: u32, x: f32, y: f32) {
4423        self.vertex_attrib(indx, x, y, 0f32, 1f32)
4424    }
4425
4426    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4427    fn VertexAttrib2fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
4428        let values = match v {
4429            Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
4430            Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
4431        };
4432        if values.len() < 2 {
4433            // https://github.com/KhronosGroup/WebGL/issues/2700
4434            return self.webgl_error(InvalidValue);
4435        }
4436        self.vertex_attrib(indx, values[0], values[1], 0f32, 1f32);
4437    }
4438
4439    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4440    fn VertexAttrib3f(&self, indx: u32, x: f32, y: f32, z: f32) {
4441        self.vertex_attrib(indx, x, y, z, 1f32)
4442    }
4443
4444    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4445    fn VertexAttrib3fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
4446        let values = match v {
4447            Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
4448            Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
4449        };
4450        if values.len() < 3 {
4451            // https://github.com/KhronosGroup/WebGL/issues/2700
4452            return self.webgl_error(InvalidValue);
4453        }
4454        self.vertex_attrib(indx, values[0], values[1], values[2], 1f32);
4455    }
4456
4457    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4458    fn VertexAttrib4f(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) {
4459        self.vertex_attrib(indx, x, y, z, w)
4460    }
4461
4462    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4463    fn VertexAttrib4fv(&self, indx: u32, v: Float32ArrayOrUnrestrictedFloatSequence) {
4464        let values = match v {
4465            Float32ArrayOrUnrestrictedFloatSequence::Float32Array(v) => v.to_vec(),
4466            Float32ArrayOrUnrestrictedFloatSequence::UnrestrictedFloatSequence(v) => v,
4467        };
4468        if values.len() < 4 {
4469            // https://github.com/KhronosGroup/WebGL/issues/2700
4470            return self.webgl_error(InvalidValue);
4471        }
4472        self.vertex_attrib(indx, values[0], values[1], values[2], values[3]);
4473    }
4474
4475    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10>
4476    fn VertexAttribPointer(
4477        &self,
4478        index: u32,
4479        size: i32,
4480        type_: u32,
4481        normalized: bool,
4482        stride: i32,
4483        offset: i64,
4484    ) {
4485        let res = match self.webgl_version() {
4486            WebGLVersion::WebGL1 => self
4487                .current_vao()
4488                .vertex_attrib_pointer(index, size, type_, normalized, stride, offset),
4489            WebGLVersion::WebGL2 => self
4490                .current_vao_webgl2()
4491                .vertex_attrib_pointer(index, size, type_, normalized, stride, offset),
4492        };
4493        handle_potential_webgl_error!(self, res);
4494    }
4495
4496    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4>
4497    fn Viewport(&self, x: i32, y: i32, width: i32, height: i32) {
4498        if width < 0 || height < 0 {
4499            return self.webgl_error(InvalidValue);
4500        }
4501
4502        self.send_command(WebGLCommand::SetViewport(x, y, width, height))
4503    }
4504
4505    // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
4506    #[allow(unsafe_code)]
4507    fn TexImage2D(
4508        &self,
4509        target: u32,
4510        level: i32,
4511        internal_format: i32,
4512        width: i32,
4513        height: i32,
4514        border: i32,
4515        format: u32,
4516        data_type: u32,
4517        pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
4518    ) -> ErrorResult {
4519        if !self.extension_manager.is_tex_type_enabled(data_type) {
4520            self.webgl_error(InvalidEnum);
4521            return Ok(());
4522        }
4523
4524        let validator = TexImage2DValidator::new(
4525            self,
4526            target,
4527            level,
4528            internal_format as u32,
4529            width,
4530            height,
4531            border,
4532            format,
4533            data_type,
4534        );
4535
4536        let TexImage2DValidatorResult {
4537            texture,
4538            target,
4539            width,
4540            height,
4541            level,
4542            border,
4543            internal_format,
4544            format,
4545            data_type,
4546        } = match validator.validate() {
4547            Ok(result) => result,
4548            Err(_) => return Ok(()), // NB: The validator sets the correct error for us.
4549        };
4550
4551        if !internal_format.compatible_data_types().contains(&data_type) {
4552            return {
4553                self.webgl_error(InvalidOperation);
4554                Ok(())
4555            };
4556        }
4557        if texture.is_immutable() {
4558            return {
4559                self.webgl_error(InvalidOperation);
4560                Ok(())
4561            };
4562        }
4563
4564        let unpacking_alignment = self.texture_unpacking_alignment.get();
4565
4566        let expected_byte_length = match self.validate_tex_image_2d_data(
4567            width,
4568            height,
4569            format,
4570            data_type,
4571            unpacking_alignment,
4572            pixels.as_ref(),
4573        ) {
4574            Ok(byte_length) => byte_length,
4575            Err(()) => return Ok(()),
4576        };
4577
4578        // If data is null, a buffer of sufficient size
4579        // initialized to 0 is passed.
4580        let buff = match *pixels {
4581            None => IpcSharedMemory::from_bytes(&vec![0u8; expected_byte_length as usize]),
4582            Some(ref data) => IpcSharedMemory::from_bytes(unsafe { data.as_slice() }),
4583        };
4584
4585        // From the WebGL spec:
4586        //
4587        //     "If pixels is non-null but its size is less than what
4588        //      is required by the specified width, height, format,
4589        //      type, and pixel storage parameters, generates an
4590        //      INVALID_OPERATION error."
4591        if buff.len() < expected_byte_length as usize {
4592            return {
4593                self.webgl_error(InvalidOperation);
4594                Ok(())
4595            };
4596        }
4597
4598        let size = Size2D::new(width, height);
4599
4600        if !self.validate_filterable_texture(
4601            &texture,
4602            target,
4603            level,
4604            internal_format,
4605            size,
4606            data_type,
4607        ) {
4608            // FIXME(nox): What is the spec for this? No error is emitted ever
4609            // by validate_filterable_texture.
4610            return Ok(());
4611        }
4612
4613        let size = Size2D::new(width, height);
4614
4615        let (alpha_treatment, y_axis_treatment) =
4616            self.get_current_unpack_state(Alpha::NotPremultiplied);
4617
4618        self.tex_image_2d(
4619            &texture,
4620            target,
4621            data_type,
4622            internal_format,
4623            format,
4624            level,
4625            border,
4626            unpacking_alignment,
4627            size,
4628            TexSource::Pixels(TexPixels::from_array(
4629                buff,
4630                size,
4631                alpha_treatment,
4632                y_axis_treatment,
4633            )),
4634        );
4635
4636        Ok(())
4637    }
4638
4639    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
4640    fn TexImage2D_(
4641        &self,
4642        target: u32,
4643        level: i32,
4644        internal_format: i32,
4645        format: u32,
4646        data_type: u32,
4647        source: TexImageSource,
4648    ) -> ErrorResult {
4649        if !self.extension_manager.is_tex_type_enabled(data_type) {
4650            self.webgl_error(InvalidEnum);
4651            return Ok(());
4652        }
4653
4654        let pixels = match self.get_image_pixels(source)? {
4655            Some(pixels) => pixels,
4656            None => return Ok(()),
4657        };
4658
4659        let validator = TexImage2DValidator::new(
4660            self,
4661            target,
4662            level,
4663            internal_format as u32,
4664            pixels.size().width as i32,
4665            pixels.size().height as i32,
4666            0,
4667            format,
4668            data_type,
4669        );
4670
4671        let TexImage2DValidatorResult {
4672            texture,
4673            target,
4674            level,
4675            border,
4676            internal_format,
4677            format,
4678            data_type,
4679            ..
4680        } = match validator.validate() {
4681            Ok(result) => result,
4682            Err(_) => return Ok(()), // NB: The validator sets the correct error for us.
4683        };
4684
4685        if !internal_format.compatible_data_types().contains(&data_type) {
4686            return {
4687                self.webgl_error(InvalidOperation);
4688                Ok(())
4689            };
4690        }
4691        if texture.is_immutable() {
4692            return {
4693                self.webgl_error(InvalidOperation);
4694                Ok(())
4695            };
4696        }
4697
4698        if !self.validate_filterable_texture(
4699            &texture,
4700            target,
4701            level,
4702            internal_format,
4703            pixels.size(),
4704            data_type,
4705        ) {
4706            // FIXME(nox): What is the spec for this? No error is emitted ever
4707            // by validate_filterable_texture.
4708            return Ok(());
4709        }
4710
4711        self.tex_image_2d(
4712            &texture,
4713            target,
4714            data_type,
4715            internal_format,
4716            format,
4717            level,
4718            border,
4719            1,
4720            pixels.size(),
4721            TexSource::Pixels(pixels),
4722        );
4723        Ok(())
4724    }
4725
4726    // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
4727    #[allow(unsafe_code)]
4728    fn TexSubImage2D(
4729        &self,
4730        target: u32,
4731        level: i32,
4732        xoffset: i32,
4733        yoffset: i32,
4734        width: i32,
4735        height: i32,
4736        format: u32,
4737        data_type: u32,
4738        pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
4739    ) -> ErrorResult {
4740        let validator = TexImage2DValidator::new(
4741            self, target, level, format, width, height, 0, format, data_type,
4742        );
4743        let TexImage2DValidatorResult {
4744            texture,
4745            target,
4746            width,
4747            height,
4748            level,
4749            format,
4750            data_type,
4751            ..
4752        } = match validator.validate() {
4753            Ok(result) => result,
4754            Err(_) => return Ok(()), // NB: The validator sets the correct error for us.
4755        };
4756
4757        let unpacking_alignment = self.texture_unpacking_alignment.get();
4758
4759        let expected_byte_length = match self.validate_tex_image_2d_data(
4760            width,
4761            height,
4762            format,
4763            data_type,
4764            unpacking_alignment,
4765            pixels.as_ref(),
4766        ) {
4767            Ok(byte_length) => byte_length,
4768            Err(()) => return Ok(()),
4769        };
4770
4771        let buff = handle_potential_webgl_error!(
4772            self,
4773            pixels
4774                .as_ref()
4775                .map(|p| IpcSharedMemory::from_bytes(unsafe { p.as_slice() }))
4776                .ok_or(InvalidValue),
4777            return Ok(())
4778        );
4779
4780        // From the WebGL spec:
4781        //
4782        //     "If pixels is non-null but its size is less than what
4783        //      is required by the specified width, height, format,
4784        //      type, and pixel storage parameters, generates an
4785        //      INVALID_OPERATION error."
4786        if buff.len() < expected_byte_length as usize {
4787            return {
4788                self.webgl_error(InvalidOperation);
4789                Ok(())
4790            };
4791        }
4792
4793        let (alpha_treatment, y_axis_treatment) =
4794            self.get_current_unpack_state(Alpha::NotPremultiplied);
4795
4796        self.tex_sub_image_2d(
4797            texture,
4798            target,
4799            level,
4800            xoffset,
4801            yoffset,
4802            format,
4803            data_type,
4804            unpacking_alignment,
4805            TexPixels::from_array(
4806                buff,
4807                Size2D::new(width, height),
4808                alpha_treatment,
4809                y_axis_treatment,
4810            ),
4811        );
4812        Ok(())
4813    }
4814
4815    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
4816    fn TexSubImage2D_(
4817        &self,
4818        target: u32,
4819        level: i32,
4820        xoffset: i32,
4821        yoffset: i32,
4822        format: u32,
4823        data_type: u32,
4824        source: TexImageSource,
4825    ) -> ErrorResult {
4826        let pixels = match self.get_image_pixels(source)? {
4827            Some(pixels) => pixels,
4828            None => return Ok(()),
4829        };
4830
4831        let validator = TexImage2DValidator::new(
4832            self,
4833            target,
4834            level,
4835            format,
4836            pixels.size().width as i32,
4837            pixels.size().height as i32,
4838            0,
4839            format,
4840            data_type,
4841        );
4842        let TexImage2DValidatorResult {
4843            texture,
4844            target,
4845            level,
4846            format,
4847            data_type,
4848            ..
4849        } = match validator.validate() {
4850            Ok(result) => result,
4851            Err(_) => return Ok(()), // NB: The validator sets the correct error for us.
4852        };
4853
4854        self.tex_sub_image_2d(
4855            texture, target, level, xoffset, yoffset, format, data_type, 1, pixels,
4856        );
4857        Ok(())
4858    }
4859
4860    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
4861    fn TexParameterf(&self, target: u32, name: u32, value: f32) {
4862        self.tex_parameter(target, name, TexParameterValue::Float(value))
4863    }
4864
4865    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8>
4866    fn TexParameteri(&self, target: u32, name: u32, value: i32) {
4867        self.tex_parameter(target, name, TexParameterValue::Int(value))
4868    }
4869
4870    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
4871    fn CheckFramebufferStatus(&self, target: u32) -> u32 {
4872        // From the GLES 2.0.25 spec, 4.4 ("Framebuffer Objects"):
4873        //
4874        //    "If target is not FRAMEBUFFER, INVALID_ENUM is
4875        //     generated. If CheckFramebufferStatus generates an
4876        //     error, 0 is returned."
4877        if target != constants::FRAMEBUFFER {
4878            self.webgl_error(InvalidEnum);
4879            return 0;
4880        }
4881
4882        match self.bound_draw_framebuffer.get() {
4883            Some(fb) => fb.check_status(),
4884            None => constants::FRAMEBUFFER_COMPLETE,
4885        }
4886    }
4887
4888    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7>
4889    fn RenderbufferStorage(&self, target: u32, internal_format: u32, width: i32, height: i32) {
4890        self.renderbuffer_storage(target, 0, internal_format, width, height)
4891    }
4892
4893    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
4894    fn FramebufferRenderbuffer(
4895        &self,
4896        target: u32,
4897        attachment: u32,
4898        renderbuffertarget: u32,
4899        rb: Option<&WebGLRenderbuffer>,
4900    ) {
4901        if let Some(rb) = rb {
4902            handle_potential_webgl_error!(self, self.validate_ownership(rb), return);
4903        }
4904
4905        if target != constants::FRAMEBUFFER || renderbuffertarget != constants::RENDERBUFFER {
4906            return self.webgl_error(InvalidEnum);
4907        }
4908
4909        match self.bound_draw_framebuffer.get() {
4910            Some(fb) => handle_potential_webgl_error!(self, fb.renderbuffer(attachment, rb)),
4911            None => self.webgl_error(InvalidOperation),
4912        };
4913    }
4914
4915    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6>
4916    fn FramebufferTexture2D(
4917        &self,
4918        target: u32,
4919        attachment: u32,
4920        textarget: u32,
4921        texture: Option<&WebGLTexture>,
4922        level: i32,
4923    ) {
4924        if let Some(texture) = texture {
4925            handle_potential_webgl_error!(self, self.validate_ownership(texture), return);
4926        }
4927
4928        if target != constants::FRAMEBUFFER {
4929            return self.webgl_error(InvalidEnum);
4930        }
4931
4932        // From the GLES 2.0.25 spec, page 113:
4933        //
4934        //     "level specifies the mipmap level of the texture image
4935        //      to be attached to the framebuffer and must be
4936        //      0. Otherwise, INVALID_VALUE is generated."
4937        if level != 0 {
4938            return self.webgl_error(InvalidValue);
4939        }
4940
4941        match self.bound_draw_framebuffer.get() {
4942            Some(fb) => handle_potential_webgl_error!(
4943                self,
4944                fb.texture2d(attachment, textarget, texture, level)
4945            ),
4946            None => self.webgl_error(InvalidOperation),
4947        };
4948    }
4949
4950    /// <https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9>
4951    fn GetAttachedShaders(&self, program: &WebGLProgram) -> Option<Vec<DomRoot<WebGLShader>>> {
4952        handle_potential_webgl_error!(self, self.validate_ownership(program), return None);
4953        handle_potential_webgl_error!(self, program.attached_shaders().map(Some), None)
4954    }
4955
4956    /// <https://immersive-web.github.io/webxr/#dom-webglrenderingcontextbase-makexrcompatible>
4957    #[cfg(feature = "webxr")]
4958    fn MakeXRCompatible(&self, can_gc: CanGc) -> Rc<Promise> {
4959        // XXXManishearth Fill in with compatibility checks when rust-webxr supports this
4960        let p = Promise::new(&self.global(), can_gc);
4961        p.resolve_native(&(), can_gc);
4962        p
4963    }
4964}
4965
4966#[derive(Default, JSTraceable, MallocSizeOf)]
4967struct Capabilities {
4968    value: Cell<CapFlags>,
4969}
4970
4971impl Capabilities {
4972    fn set(&self, cap: u32, set: bool) -> WebGLResult<bool> {
4973        let cap = CapFlags::from_enum(cap)?;
4974        let mut value = self.value.get();
4975        if value.contains(cap) == set {
4976            return Ok(false);
4977        }
4978        value.set(cap, set);
4979        self.value.set(value);
4980        Ok(true)
4981    }
4982
4983    fn is_enabled(&self, cap: u32) -> WebGLResult<bool> {
4984        Ok(self.value.get().contains(CapFlags::from_enum(cap)?))
4985    }
4986}
4987
4988impl Default for CapFlags {
4989    fn default() -> Self {
4990        CapFlags::DITHER
4991    }
4992}
4993
4994macro_rules! capabilities {
4995    ($name:ident, $next:ident, $($rest:ident,)*) => {
4996        capabilities!($name, $next, $($rest,)* [$name = 1;]);
4997    };
4998    ($prev:ident, $name:ident, $($rest:ident,)* [$($tt:tt)*]) => {
4999        capabilities!($name, $($rest,)* [$($tt)* $name = Self::$prev.bits() << 1;]);
5000    };
5001    ($prev:ident, [$($name:ident = $value:expr;)*]) => {
5002        #[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
5003        pub(crate) struct CapFlags(u16);
5004
5005        bitflags! {
5006            impl CapFlags: u16 {
5007                $(const $name = $value;)*
5008            }
5009        }
5010
5011        impl CapFlags {
5012            fn from_enum(cap: u32) -> WebGLResult<Self> {
5013                match cap {
5014                    $(constants::$name => Ok(Self::$name),)*
5015                    _ => Err(InvalidEnum),
5016                }
5017            }
5018        }
5019    };
5020}
5021
5022capabilities! {
5023    BLEND,
5024    CULL_FACE,
5025    DEPTH_TEST,
5026    DITHER,
5027    POLYGON_OFFSET_FILL,
5028    SAMPLE_ALPHA_TO_COVERAGE,
5029    SAMPLE_COVERAGE,
5030    SCISSOR_TEST,
5031    STENCIL_TEST,
5032}
5033
5034#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
5035#[derive(JSTraceable, MallocSizeOf)]
5036pub(crate) struct Textures {
5037    active_unit: Cell<u32>,
5038    units: Box<[TextureUnit]>,
5039}
5040
5041impl Textures {
5042    fn new(max_combined_textures: u32) -> Self {
5043        Self {
5044            active_unit: Default::default(),
5045            units: (0..max_combined_textures)
5046                .map(|_| Default::default())
5047                .collect::<Vec<_>>()
5048                .into(),
5049        }
5050    }
5051
5052    pub(crate) fn active_unit_enum(&self) -> u32 {
5053        self.active_unit.get() + constants::TEXTURE0
5054    }
5055
5056    fn set_active_unit_enum(&self, index: u32) -> WebGLResult<()> {
5057        if index < constants::TEXTURE0 || (index - constants::TEXTURE0) as usize > self.units.len()
5058        {
5059            return Err(InvalidEnum);
5060        }
5061        self.active_unit.set(index - constants::TEXTURE0);
5062        Ok(())
5063    }
5064
5065    pub(crate) fn active_texture_slot(
5066        &self,
5067        target: u32,
5068        webgl_version: WebGLVersion,
5069    ) -> WebGLResult<&MutNullableDom<WebGLTexture>> {
5070        let active_unit = self.active_unit();
5071        let is_webgl2 = webgl_version == WebGLVersion::WebGL2;
5072        match target {
5073            constants::TEXTURE_2D => Ok(&active_unit.tex_2d),
5074            constants::TEXTURE_CUBE_MAP => Ok(&active_unit.tex_cube_map),
5075            WebGL2RenderingContextConstants::TEXTURE_2D_ARRAY if is_webgl2 => {
5076                Ok(&active_unit.tex_2d_array)
5077            },
5078            WebGL2RenderingContextConstants::TEXTURE_3D if is_webgl2 => Ok(&active_unit.tex_3d),
5079            _ => Err(InvalidEnum),
5080        }
5081    }
5082
5083    pub(crate) fn active_texture_for_image_target(
5084        &self,
5085        target: TexImageTarget,
5086    ) -> Option<DomRoot<WebGLTexture>> {
5087        let active_unit = self.active_unit();
5088        match target {
5089            TexImageTarget::Texture2D => active_unit.tex_2d.get(),
5090            TexImageTarget::Texture2DArray => active_unit.tex_2d_array.get(),
5091            TexImageTarget::Texture3D => active_unit.tex_3d.get(),
5092            TexImageTarget::CubeMap |
5093            TexImageTarget::CubeMapPositiveX |
5094            TexImageTarget::CubeMapNegativeX |
5095            TexImageTarget::CubeMapPositiveY |
5096            TexImageTarget::CubeMapNegativeY |
5097            TexImageTarget::CubeMapPositiveZ |
5098            TexImageTarget::CubeMapNegativeZ => active_unit.tex_cube_map.get(),
5099        }
5100    }
5101
5102    fn active_unit(&self) -> &TextureUnit {
5103        &self.units[self.active_unit.get() as usize]
5104    }
5105
5106    fn iter(&self) -> impl Iterator<Item = (u32, &TextureUnit)> {
5107        self.units
5108            .iter()
5109            .enumerate()
5110            .map(|(index, unit)| (index as u32 + constants::TEXTURE0, unit))
5111    }
5112}
5113
5114#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
5115#[derive(Default, JSTraceable, MallocSizeOf)]
5116struct TextureUnit {
5117    tex_2d: MutNullableDom<WebGLTexture>,
5118    tex_cube_map: MutNullableDom<WebGLTexture>,
5119    tex_2d_array: MutNullableDom<WebGLTexture>,
5120    tex_3d: MutNullableDom<WebGLTexture>,
5121}
5122
5123impl TextureUnit {
5124    fn unbind(&self, texture: &WebGLTexture) -> Option<u32> {
5125        let fields = [
5126            (&self.tex_2d, constants::TEXTURE_2D),
5127            (&self.tex_cube_map, constants::TEXTURE_CUBE_MAP),
5128            (
5129                &self.tex_2d_array,
5130                WebGL2RenderingContextConstants::TEXTURE_2D_ARRAY,
5131            ),
5132            (&self.tex_3d, WebGL2RenderingContextConstants::TEXTURE_3D),
5133        ];
5134        for &(slot, target) in &fields {
5135            if slot.get().is_some_and(|t| texture == &*t) {
5136                slot.set(None);
5137                return Some(target);
5138            }
5139        }
5140        None
5141    }
5142}
5143
5144pub(crate) struct TexPixels {
5145    data: IpcSharedMemory,
5146    size: Size2D<u32>,
5147    pixel_format: Option<PixelFormat>,
5148    alpha_treatment: Option<AlphaTreatment>,
5149    y_axis_treatment: YAxisTreatment,
5150}
5151
5152impl TexPixels {
5153    fn new(
5154        data: IpcSharedMemory,
5155        size: Size2D<u32>,
5156        pixel_format: PixelFormat,
5157        alpha_treatment: Option<AlphaTreatment>,
5158        y_axis_treatment: YAxisTreatment,
5159    ) -> Self {
5160        Self {
5161            data,
5162            size,
5163            pixel_format: Some(pixel_format),
5164            alpha_treatment,
5165            y_axis_treatment,
5166        }
5167    }
5168
5169    pub(crate) fn from_array(
5170        data: IpcSharedMemory,
5171        size: Size2D<u32>,
5172        alpha_treatment: Option<AlphaTreatment>,
5173        y_axis_treatment: YAxisTreatment,
5174    ) -> Self {
5175        Self {
5176            data,
5177            size,
5178            pixel_format: None,
5179            alpha_treatment,
5180            y_axis_treatment,
5181        }
5182    }
5183
5184    pub(crate) fn size(&self) -> Size2D<u32> {
5185        self.size
5186    }
5187
5188    pub(crate) fn pixel_format(&self) -> Option<PixelFormat> {
5189        self.pixel_format
5190    }
5191
5192    pub(crate) fn alpha_treatment(&self) -> Option<AlphaTreatment> {
5193        self.alpha_treatment
5194    }
5195
5196    pub(crate) fn y_axis_treatment(&self) -> YAxisTreatment {
5197        self.y_axis_treatment
5198    }
5199
5200    pub(crate) fn into_shared_memory(self) -> IpcSharedMemory {
5201        self.data
5202    }
5203}
5204
5205pub(crate) enum TexSource {
5206    Pixels(TexPixels),
5207    BufferOffset(i64),
5208}
5209
5210#[derive(JSTraceable)]
5211pub(crate) struct WebGLCommandSender {
5212    #[no_trace]
5213    sender: WebGLChan,
5214}
5215
5216impl WebGLCommandSender {
5217    pub(crate) fn new(sender: WebGLChan) -> WebGLCommandSender {
5218        WebGLCommandSender { sender }
5219    }
5220
5221    pub(crate) fn send(&self, msg: WebGLMsg) -> WebGLSendResult {
5222        self.sender.send(msg)
5223    }
5224}
5225
5226fn array_buffer_type_to_sized_type(type_: Type) -> Option<SizedDataType> {
5227    match type_ {
5228        Type::Uint8 | Type::Uint8Clamped => Some(SizedDataType::Uint8),
5229        Type::Uint16 => Some(SizedDataType::Uint16),
5230        Type::Uint32 => Some(SizedDataType::Uint32),
5231        Type::Int8 => Some(SizedDataType::Int8),
5232        Type::Int16 => Some(SizedDataType::Int16),
5233        Type::Int32 => Some(SizedDataType::Int32),
5234        Type::Float32 => Some(SizedDataType::Float32),
5235        Type::Float16 |
5236        Type::Float64 |
5237        Type::BigInt64 |
5238        Type::BigUint64 |
5239        Type::MaxTypedArrayViewType |
5240        Type::Int64 |
5241        Type::Simd128 => None,
5242    }
5243}