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