Skip to main content

script/dom/webgl/
webglprogram.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
5// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
6use std::cell::{Cell, RefCell};
7use std::collections::HashSet;
8
9use dom_struct::dom_struct;
10use js::context::JSContext;
11use script_bindings::cell::{DomRefCell, Ref};
12use script_bindings::reflector::reflect_dom_object;
13use script_bindings::weakref::WeakRef;
14use servo_base::text::Utf8CodeUnits;
15use servo_canvas_traits::webgl::{
16    ActiveAttribInfo, ActiveUniformBlockInfo, ActiveUniformInfo, WebGLCommand, WebGLError,
17    WebGLProgramId, WebGLResult, webgl_channel,
18};
19
20use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants2;
21use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
22use crate::dom::bindings::inheritance::Castable;
23use crate::dom::bindings::reflector::DomGlobal;
24use crate::dom::bindings::root::{DomRoot, MutNullableDom};
25use crate::dom::bindings::str::DOMString;
26use crate::dom::webgl::webglactiveinfo::WebGLActiveInfo;
27use crate::dom::webgl::webglobject::WebGLObject;
28use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
29use crate::dom::webgl::webglshader::WebGLShader;
30use crate::dom::webgl::webgluniformlocation::WebGLUniformLocation;
31use crate::dom::webglrenderingcontext::capture_webgl_backtrace;
32
33#[derive(JSTraceable, MallocSizeOf)]
34struct DroppableWebGLProgram {
35    #[no_trace]
36    id: WebGLProgramId,
37    context: WeakRef<WebGLRenderingContext>,
38    fragment_shader: Option<WeakRef<WebGLShader>>,
39    vertex_shader: Option<WeakRef<WebGLShader>>,
40    marked_for_deletion: bool,
41    is_in_use: bool,
42}
43
44impl DroppableWebGLProgram {
45    fn new(id: WebGLProgramId, context: &WebGLRenderingContext) -> Self {
46        Self {
47            id,
48            context: WeakRef::new(context),
49            fragment_shader: None,
50            vertex_shader: None,
51            marked_for_deletion: Default::default(),
52            is_in_use: Default::default(),
53        }
54    }
55}
56
57impl DroppableWebGLProgram {
58    fn attach_shader<'a>(&mut self, shader: &'a WebGLShader) -> WebGLResult<&'a WebGLShader> {
59        if self.is_deleted() || shader.is_deleted() {
60            return Err(WebGLError::InvalidOperation);
61        }
62        let shader_slot = match shader.gl_type() {
63            constants::FRAGMENT_SHADER => &mut self.fragment_shader,
64            constants::VERTEX_SHADER => &mut self.vertex_shader,
65            _ => {
66                error!("detachShader: Unexpected shader type");
67                return Err(WebGLError::InvalidValue);
68            },
69        };
70
71        if shader_slot.is_some() {
72            return Err(WebGLError::InvalidOperation);
73        }
74
75        *shader_slot = Some(WeakRef::new(shader));
76        shader.increment_attached_counter();
77
78        self.send_command(WebGLCommand::AttachShader(self.id, shader.id()));
79
80        Ok(shader)
81    }
82
83    fn detach_shader<'a>(&mut self, shader: &'a WebGLShader) -> WebGLResult<&'a WebGLShader> {
84        if self.is_deleted() {
85            return Err(WebGLError::InvalidOperation);
86        }
87        let shader_slot = match shader.gl_type() {
88            constants::FRAGMENT_SHADER => &mut self.fragment_shader,
89            constants::VERTEX_SHADER => &mut self.vertex_shader,
90            _ => return Err(WebGLError::InvalidValue),
91        };
92
93        match shader_slot {
94            Some(attached_shader) => match attached_shader.root() {
95                Some(root) => {
96                    if root.id() != shader.id() {
97                        return Err(WebGLError::InvalidOperation);
98                    }
99                },
100                None => return Err(WebGLError::InvalidOperation),
101            },
102            None => return Err(WebGLError::InvalidOperation),
103        }
104
105        *shader_slot = None;
106        shader.decrement_attached_counter();
107
108        self.send_command(WebGLCommand::DetachShader(self.id, shader.id()));
109
110        Ok(shader)
111    }
112
113    fn detach_shaders(&mut self) {
114        if let Some(ref mut shader) = self.fragment_shader {
115            if let Some(root) = shader.root() {
116                root.decrement_attached_counter();
117                self.send_command(WebGLCommand::DetachShader(self.id, root.id()));
118            }
119            self.fragment_shader = None;
120        }
121        if let Some(ref mut shader) = self.vertex_shader {
122            if let Some(root) = shader.root() {
123                root.decrement_attached_counter();
124                self.send_command(WebGLCommand::DetachShader(self.id, root.id()));
125            }
126            self.vertex_shader = None;
127        }
128    }
129
130    fn is_deleted(&self) -> bool {
131        self.marked_for_deletion && !self.is_in_use
132    }
133
134    fn send_command(&self, command: WebGLCommand) {
135        self.send_with_fallibility(command, Operation::Infallible);
136    }
137
138    fn send_with_fallibility(&self, command: WebGLCommand, fallibility: Operation) {
139        if let Some(root) = self.context.root() {
140            let result = root.sender().send(command, capture_webgl_backtrace());
141            if matches!(fallibility, Operation::Infallible) {
142                result.expect("Operation failed");
143            }
144        }
145    }
146
147    fn mark_for_deletion(&mut self, operation_fallibility: Operation) {
148        if self.marked_for_deletion {
149            return;
150        }
151        self.marked_for_deletion = true;
152        self.send_with_fallibility(WebGLCommand::DeleteProgram(self.id), operation_fallibility);
153        if self.is_deleted() {
154            self.detach_shaders();
155        }
156    }
157
158    fn in_use(&mut self, value: bool) {
159        if self.is_in_use == value {
160            return;
161        }
162        self.is_in_use = value;
163        if self.is_deleted() {
164            self.detach_shaders();
165        }
166    }
167}
168
169impl Drop for DroppableWebGLProgram {
170    fn drop(&mut self) {
171        self.in_use(false);
172        self.mark_for_deletion(Operation::Fallible);
173    }
174}
175
176#[dom_struct]
177pub(crate) struct WebGLProgram {
178    webgl_object: WebGLObject,
179    link_called: Cell<bool>,
180    linked: Cell<bool>,
181    link_generation: Cell<u64>,
182    fragment_shader: MutNullableDom<WebGLShader>,
183    vertex_shader: MutNullableDom<WebGLShader>,
184    #[no_trace]
185    active_attribs: DomRefCell<Box<[ActiveAttribInfo]>>,
186    #[no_trace]
187    active_uniforms: DomRefCell<Box<[ActiveUniformInfo]>>,
188    #[no_trace]
189    active_uniform_blocks: DomRefCell<Box<[ActiveUniformBlockInfo]>>,
190    transform_feedback_varyings_length: Cell<i32>,
191    transform_feedback_mode: Cell<i32>,
192    droppable: RefCell<DroppableWebGLProgram>,
193}
194
195impl WebGLProgram {
196    fn new_inherited(context: &WebGLRenderingContext, id: WebGLProgramId) -> Self {
197        Self {
198            webgl_object: WebGLObject::new_inherited(context),
199            link_called: Default::default(),
200            linked: Default::default(),
201            link_generation: Default::default(),
202            fragment_shader: Default::default(),
203            vertex_shader: Default::default(),
204            active_attribs: DomRefCell::new(vec![].into()),
205            active_uniforms: DomRefCell::new(vec![].into()),
206            active_uniform_blocks: DomRefCell::new(vec![].into()),
207            transform_feedback_varyings_length: Default::default(),
208            transform_feedback_mode: Default::default(),
209            droppable: RefCell::new(DroppableWebGLProgram::new(id, context)),
210        }
211    }
212
213    pub(crate) fn maybe_new(
214        cx: &mut JSContext,
215        context: &WebGLRenderingContext,
216    ) -> Option<DomRoot<Self>> {
217        let (sender, receiver) = webgl_channel().unwrap();
218        context.send_command(WebGLCommand::CreateProgram(sender));
219        receiver
220            .recv()
221            .unwrap()
222            .map(|id| WebGLProgram::new(cx, context, id))
223    }
224
225    pub(crate) fn new(
226        cx: &mut JSContext,
227        context: &WebGLRenderingContext,
228        id: WebGLProgramId,
229    ) -> DomRoot<Self> {
230        reflect_dom_object(
231            cx,
232            Box::new(WebGLProgram::new_inherited(context, id)),
233            &*context.global(),
234        )
235    }
236}
237
238impl WebGLProgram {
239    pub(crate) fn id(&self) -> WebGLProgramId {
240        self.droppable.borrow().id
241    }
242
243    /// glDeleteProgram
244    pub(crate) fn mark_for_deletion(&self, operation_fallibility: Operation) {
245        if self.is_marked_for_deletion() {
246            return;
247        }
248        self.set_marked_for_deletion(true);
249        self.upcast().send_with_fallibility(
250            WebGLCommand::DeleteProgram(self.id()),
251            operation_fallibility,
252        );
253        if self.is_deleted() {
254            self.detach_shaders();
255        }
256    }
257
258    pub(crate) fn in_use(&self, value: bool) {
259        if self.is_in_use() == value {
260            return;
261        }
262        self.set_is_in_use(value);
263        if self.is_deleted() {
264            self.detach_shaders();
265        }
266    }
267
268    fn detach_shaders(&self) {
269        assert!(self.is_deleted());
270        self.droppable.borrow_mut().detach_shaders();
271        if self.fragment_shader.get().is_some() {
272            self.fragment_shader.set(None);
273        }
274        if self.vertex_shader.get().is_some() {
275            self.vertex_shader.set(None);
276        }
277    }
278
279    pub(crate) fn is_in_use(&self) -> bool {
280        self.droppable.borrow().is_in_use
281    }
282
283    pub(crate) fn is_marked_for_deletion(&self) -> bool {
284        self.droppable.borrow().marked_for_deletion
285    }
286
287    pub(crate) fn is_deleted(&self) -> bool {
288        self.is_marked_for_deletion() && !self.is_in_use()
289    }
290
291    pub(crate) fn is_linked(&self) -> bool {
292        self.linked.get()
293    }
294
295    /// glLinkProgram
296    pub(crate) fn link(&self) -> WebGLResult<()> {
297        self.linked.set(false);
298        self.link_generation
299            .set(self.link_generation.get().checked_add(1).unwrap());
300        *self.active_attribs.borrow_mut() = Box::new([]);
301        *self.active_uniforms.borrow_mut() = Box::new([]);
302        *self.active_uniform_blocks.borrow_mut() = Box::new([]);
303
304        match self.fragment_shader.get() {
305            Some(ref shader) if shader.successfully_compiled() => {},
306            _ => return Ok(()), // callers use gl.LINK_STATUS to check link errors
307        }
308
309        match self.vertex_shader.get() {
310            Some(ref shader) if shader.successfully_compiled() => {},
311            _ => return Ok(()), // callers use gl.LINK_STATUS to check link errors
312        }
313
314        let (sender, receiver) = webgl_channel().unwrap();
315        self.upcast()
316            .send_command(WebGLCommand::LinkProgram(self.id(), sender));
317        let link_info = receiver.recv().unwrap();
318
319        {
320            let mut used_locs = HashSet::new();
321            let mut used_names = HashSet::new();
322            for active_attrib in &*link_info.active_attribs {
323                let Some(location) = active_attrib.location else {
324                    continue;
325                };
326                let columns = match active_attrib.type_ {
327                    constants::FLOAT_MAT2 => 2,
328                    constants::FLOAT_MAT3 => 3,
329                    constants::FLOAT_MAT4 => 4,
330                    _ => 1,
331                };
332                assert!(used_names.insert(&*active_attrib.name));
333                for column in 0..columns {
334                    // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.31
335                    if !used_locs.insert(location + column) {
336                        return Ok(());
337                    }
338                }
339            }
340            for active_uniform in &*link_info.active_uniforms {
341                // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.41
342                if !used_names.insert(&*active_uniform.base_name) {
343                    return Ok(());
344                }
345            }
346        }
347
348        self.linked.set(link_info.linked);
349        self.link_called.set(true);
350        self.transform_feedback_varyings_length
351            .set(link_info.transform_feedback_length);
352        self.transform_feedback_mode
353            .set(link_info.transform_feedback_mode);
354        *self.active_attribs.borrow_mut() = link_info.active_attribs;
355        *self.active_uniforms.borrow_mut() = link_info.active_uniforms;
356        *self.active_uniform_blocks.borrow_mut() = link_info.active_uniform_blocks;
357        Ok(())
358    }
359
360    pub(crate) fn active_attribs(&self) -> Ref<'_, [ActiveAttribInfo]> {
361        Ref::map(self.active_attribs.borrow(), |attribs| &**attribs)
362    }
363
364    pub(crate) fn active_uniforms(&self) -> Ref<'_, [ActiveUniformInfo]> {
365        Ref::map(self.active_uniforms.borrow(), |uniforms| &**uniforms)
366    }
367
368    pub(crate) fn active_uniform_blocks(&self) -> Ref<'_, [ActiveUniformBlockInfo]> {
369        Ref::map(self.active_uniform_blocks.borrow(), |blocks| &**blocks)
370    }
371
372    /// glValidateProgram
373    pub(crate) fn validate(&self) -> WebGLResult<()> {
374        if self.is_deleted() {
375            return Err(WebGLError::InvalidOperation);
376        }
377        self.upcast()
378            .send_command(WebGLCommand::ValidateProgram(self.id()));
379        Ok(())
380    }
381
382    /// glAttachShader
383    pub(crate) fn attach_shader(&self, shader: &WebGLShader) -> WebGLResult<()> {
384        match self.droppable.borrow_mut().attach_shader(shader) {
385            Ok(shader) => {
386                let shader_slot = match shader.gl_type() {
387                    constants::FRAGMENT_SHADER => &self.fragment_shader,
388                    constants::VERTEX_SHADER => &self.vertex_shader,
389                    _ => {
390                        error!("attach_shader: Unexpected shader type");
391                        return Err(WebGLError::InvalidValue);
392                    },
393                };
394
395                shader_slot.set(Some(shader));
396
397                Ok(())
398            },
399            Err(e) => Err(e),
400        }
401    }
402
403    /// glDetachShader
404    pub(crate) fn detach_shader(&self, shader: &WebGLShader) -> WebGLResult<()> {
405        match self.droppable.borrow_mut().detach_shader(shader) {
406            Ok(shader) => {
407                let shader_slot = match shader.gl_type() {
408                    constants::FRAGMENT_SHADER => &self.fragment_shader,
409                    constants::VERTEX_SHADER => &self.vertex_shader,
410                    _ => {
411                        error!("detach_shader: Unexpected shader type");
412                        return Err(WebGLError::InvalidValue);
413                    },
414                };
415
416                shader_slot.set(None);
417
418                Ok(())
419            },
420            Err(e) => Err(e),
421        }
422    }
423
424    /// glBindAttribLocation
425    pub(crate) fn bind_attrib_location(&self, index: u32, name: DOMString) -> WebGLResult<()> {
426        if self.is_deleted() {
427            return Err(WebGLError::InvalidOperation);
428        }
429
430        if !validate_glsl_name(&name)? {
431            return Ok(());
432        }
433        if name.starts_with_str("gl_") {
434            return Err(WebGLError::InvalidOperation);
435        }
436
437        self.upcast().send_command(WebGLCommand::BindAttribLocation(
438            self.id(),
439            index,
440            name.into(),
441        ));
442        Ok(())
443    }
444
445    pub(crate) fn get_active_uniform(
446        &self,
447        cx: &mut JSContext,
448        index: u32,
449    ) -> WebGLResult<DomRoot<WebGLActiveInfo>> {
450        if self.is_deleted() {
451            return Err(WebGLError::InvalidValue);
452        }
453        let uniforms = self.active_uniforms.borrow();
454        let data = uniforms
455            .get(index as usize)
456            .ok_or(WebGLError::InvalidValue)?;
457        Ok(WebGLActiveInfo::new(
458            cx,
459            self.global().as_window(),
460            data.size.unwrap_or(1),
461            data.type_,
462            data.name().into(),
463        ))
464    }
465
466    /// glGetActiveAttrib
467    pub(crate) fn get_active_attrib(
468        &self,
469        cx: &mut JSContext,
470        index: u32,
471    ) -> WebGLResult<DomRoot<WebGLActiveInfo>> {
472        if self.is_deleted() {
473            return Err(WebGLError::InvalidValue);
474        }
475        let attribs = self.active_attribs.borrow();
476        let data = attribs
477            .get(index as usize)
478            .ok_or(WebGLError::InvalidValue)?;
479        Ok(WebGLActiveInfo::new(
480            cx,
481            self.global().as_window(),
482            data.size,
483            data.type_,
484            data.name.clone().into(),
485        ))
486    }
487
488    /// glGetAttribLocation
489    pub(crate) fn get_attrib_location(&self, name: DOMString) -> WebGLResult<i32> {
490        if !self.is_linked() || self.is_deleted() {
491            return Err(WebGLError::InvalidOperation);
492        }
493
494        if !validate_glsl_name(&name)? {
495            return Ok(-1);
496        }
497        if name.starts_with_str("gl_") {
498            return Ok(-1);
499        }
500
501        let location = self
502            .active_attribs
503            .borrow()
504            .iter()
505            .find(|attrib| *attrib.name == name)
506            .and_then(|attrib| attrib.location.map(|l| l as i32))
507            .unwrap_or(-1);
508        Ok(location)
509    }
510
511    /// glGetFragDataLocation
512    pub(crate) fn get_frag_data_location(&self, name: DOMString) -> WebGLResult<i32> {
513        if !self.is_linked() || self.is_deleted() {
514            return Err(WebGLError::InvalidOperation);
515        }
516
517        if !validate_glsl_name(&name)? {
518            return Ok(-1);
519        }
520        if name.starts_with_str("gl_") {
521            return Ok(-1);
522        }
523
524        let (sender, receiver) = webgl_channel().unwrap();
525        self.upcast()
526            .send_command(WebGLCommand::GetFragDataLocation(
527                self.id(),
528                name.into(),
529                sender,
530            ));
531        Ok(receiver.recv().unwrap())
532    }
533
534    /// glGetUniformLocation
535    pub(crate) fn get_uniform_location(
536        &self,
537        cx: &mut JSContext,
538        name: DOMString,
539    ) -> WebGLResult<Option<DomRoot<WebGLUniformLocation>>> {
540        if !self.is_linked() || self.is_deleted() {
541            return Err(WebGLError::InvalidOperation);
542        }
543
544        if !validate_glsl_name(&name)? {
545            return Ok(None);
546        }
547        if name.starts_with_str("gl_") {
548            return Ok(None);
549        }
550
551        let (size, type_) = {
552            let (base_name, array_index) = match parse_uniform_name(&name) {
553                Some((name, index)) if index.is_none_or(|i| i >= 0) => (name, index),
554                _ => return Ok(None),
555            };
556
557            let uniforms = self.active_uniforms.borrow();
558            match uniforms
559                .iter()
560                .find(|attrib| *attrib.base_name == base_name)
561            {
562                Some(uniform) if array_index.is_none() || array_index < uniform.size => (
563                    uniform
564                        .size
565                        .map(|size| size - array_index.unwrap_or_default()),
566                    uniform.type_,
567                ),
568                _ => return Ok(None),
569            }
570        };
571
572        let (sender, receiver) = webgl_channel().unwrap();
573        self.upcast().send_command(WebGLCommand::GetUniformLocation(
574            self.id(),
575            name.into(),
576            sender,
577        ));
578        let location = receiver.recv().unwrap();
579        let context_id = self.upcast().context_id();
580
581        Ok(Some(WebGLUniformLocation::new(
582            cx,
583            self.global().as_window(),
584            location,
585            context_id,
586            self.id(),
587            self.link_generation.get(),
588            size,
589            type_,
590        )))
591    }
592
593    pub(crate) fn get_uniform_block_index(&self, name: DOMString) -> WebGLResult<u32> {
594        if !self.link_called.get() || self.is_deleted() {
595            return Err(WebGLError::InvalidOperation);
596        }
597
598        if !validate_glsl_name(&name)? {
599            return Ok(constants2::INVALID_INDEX);
600        }
601
602        let (sender, receiver) = webgl_channel().unwrap();
603        self.upcast()
604            .send_command(WebGLCommand::GetUniformBlockIndex(
605                self.id(),
606                name.into(),
607                sender,
608            ));
609        Ok(receiver.recv().unwrap())
610    }
611
612    pub(crate) fn get_uniform_indices(&self, names: Vec<DOMString>) -> WebGLResult<Vec<u32>> {
613        if !self.link_called.get() || self.is_deleted() {
614            return Err(WebGLError::InvalidOperation);
615        }
616
617        let validation_errors = names.iter().map(validate_glsl_name).collect::<Vec<_>>();
618        let first_validation_error = validation_errors.iter().find(|result| result.is_err());
619        if let Some(error) = first_validation_error {
620            return Err(error.unwrap_err());
621        }
622
623        let names = names
624            .iter()
625            .map(|name| name.to_string())
626            .collect::<Vec<_>>();
627
628        let (sender, receiver) = webgl_channel().unwrap();
629        self.upcast()
630            .send_command(WebGLCommand::GetUniformIndices(self.id(), names, sender));
631        Ok(receiver.recv().unwrap())
632    }
633
634    pub(crate) fn get_active_uniforms(
635        &self,
636        indices: Vec<u32>,
637        pname: u32,
638    ) -> WebGLResult<Vec<i32>> {
639        if !self.is_linked() || self.is_deleted() {
640            return Err(WebGLError::InvalidOperation);
641        }
642
643        match pname {
644            constants2::UNIFORM_TYPE |
645            constants2::UNIFORM_SIZE |
646            constants2::UNIFORM_BLOCK_INDEX |
647            constants2::UNIFORM_OFFSET |
648            constants2::UNIFORM_ARRAY_STRIDE |
649            constants2::UNIFORM_MATRIX_STRIDE |
650            constants2::UNIFORM_IS_ROW_MAJOR => {},
651            _ => return Err(WebGLError::InvalidEnum),
652        }
653
654        if indices.len() > self.active_uniforms.borrow().len() {
655            return Err(WebGLError::InvalidValue);
656        }
657
658        let (sender, receiver) = webgl_channel().unwrap();
659        self.upcast().send_command(WebGLCommand::GetActiveUniforms(
660            self.id(),
661            indices,
662            pname,
663            sender,
664        ));
665        Ok(receiver.recv().unwrap())
666    }
667
668    pub(crate) fn get_active_uniform_block_parameter(
669        &self,
670        block_index: u32,
671        pname: u32,
672    ) -> WebGLResult<Vec<i32>> {
673        if !self.link_called.get() || self.is_deleted() {
674            return Err(WebGLError::InvalidOperation);
675        }
676
677        if block_index as usize >= self.active_uniform_blocks.borrow().len() {
678            return Err(WebGLError::InvalidValue);
679        }
680
681        match pname {
682            constants2::UNIFORM_BLOCK_BINDING |
683            constants2::UNIFORM_BLOCK_DATA_SIZE |
684            constants2::UNIFORM_BLOCK_ACTIVE_UNIFORMS |
685            constants2::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES |
686            constants2::UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER |
687            constants2::UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER => {},
688            _ => return Err(WebGLError::InvalidEnum),
689        }
690
691        let (sender, receiver) = webgl_channel().unwrap();
692        self.upcast()
693            .send_command(WebGLCommand::GetActiveUniformBlockParameter(
694                self.id(),
695                block_index,
696                pname,
697                sender,
698            ));
699        Ok(receiver.recv().unwrap())
700    }
701
702    pub(crate) fn get_active_uniform_block_name(&self, block_index: u32) -> WebGLResult<String> {
703        if !self.link_called.get() || self.is_deleted() {
704            return Err(WebGLError::InvalidOperation);
705        }
706
707        if block_index as usize >= self.active_uniform_blocks.borrow().len() {
708            return Err(WebGLError::InvalidValue);
709        }
710
711        let (sender, receiver) = webgl_channel().unwrap();
712        self.upcast()
713            .send_command(WebGLCommand::GetActiveUniformBlockName(
714                self.id(),
715                block_index,
716                sender,
717            ));
718        Ok(receiver.recv().unwrap())
719    }
720
721    pub(crate) fn bind_uniform_block(
722        &self,
723        block_index: u32,
724        block_binding: u32,
725    ) -> WebGLResult<()> {
726        if block_index as usize >= self.active_uniform_blocks.borrow().len() {
727            return Err(WebGLError::InvalidValue);
728        }
729
730        let mut active_uniforms = self.active_uniforms.borrow_mut();
731        if active_uniforms.len() > block_binding as usize {
732            active_uniforms[block_binding as usize].bind_index = Some(block_binding);
733        }
734
735        self.upcast()
736            .send_command(WebGLCommand::UniformBlockBinding(
737                self.id(),
738                block_index,
739                block_binding,
740            ));
741        Ok(())
742    }
743
744    /// glGetProgramInfoLog
745    pub(crate) fn get_info_log(&self) -> WebGLResult<String> {
746        if self.is_deleted() {
747            return Err(WebGLError::InvalidValue);
748        }
749        if self.link_called.get() {
750            let shaders_compiled = match (self.fragment_shader.get(), self.vertex_shader.get()) {
751                (Some(fs), Some(vs)) => fs.successfully_compiled() && vs.successfully_compiled(),
752                _ => false,
753            };
754            if !shaders_compiled {
755                return Ok("One or more shaders failed to compile".to_string());
756            }
757        }
758        let (sender, receiver) = webgl_channel().unwrap();
759        self.upcast()
760            .send_command(WebGLCommand::GetProgramInfoLog(self.id(), sender));
761        Ok(receiver.recv().unwrap())
762    }
763
764    pub(crate) fn attached_shaders(&self) -> WebGLResult<Vec<DomRoot<WebGLShader>>> {
765        if self.is_marked_for_deletion() {
766            return Err(WebGLError::InvalidValue);
767        }
768        Ok(
769            match (self.vertex_shader.get(), self.fragment_shader.get()) {
770                (Some(vertex_shader), Some(fragment_shader)) => {
771                    vec![vertex_shader, fragment_shader]
772                },
773                (Some(shader), None) | (None, Some(shader)) => vec![shader],
774                (None, None) => vec![],
775            },
776        )
777    }
778
779    pub(crate) fn link_generation(&self) -> u64 {
780        self.link_generation.get()
781    }
782
783    pub(crate) fn transform_feedback_varyings_length(&self) -> i32 {
784        self.transform_feedback_varyings_length.get()
785    }
786
787    pub(crate) fn transform_feedback_buffer_mode(&self) -> i32 {
788        self.transform_feedback_mode.get()
789    }
790
791    fn set_marked_for_deletion(&self, value: bool) {
792        self.droppable.borrow_mut().marked_for_deletion = value
793    }
794
795    fn set_is_in_use(&self, value: bool) {
796        self.droppable.borrow_mut().is_in_use = value
797    }
798}
799
800fn validate_glsl_name(name: &DOMString) -> WebGLResult<bool> {
801    if name.is_empty() {
802        return Ok(false);
803    }
804    if name.len_utf8() > MAX_UNIFORM_AND_ATTRIBUTE_LEN {
805        return Err(WebGLError::InvalidValue);
806    }
807    for c in name.str().chars() {
808        validate_glsl_char(c)?;
809    }
810    if name.starts_with_str("webgl_") || name.starts_with_str("_webgl_") {
811        return Err(WebGLError::InvalidOperation);
812    }
813    Ok(true)
814}
815
816fn validate_glsl_char(c: char) -> WebGLResult<()> {
817    match c {
818        'a'..='z' |
819        'A'..='Z' |
820        '0'..='9' |
821        ' ' |
822        '\t' |
823        '\u{11}' |
824        '\u{12}' |
825        '\r' |
826        '\n' |
827        '_' |
828        '.' |
829        '+' |
830        '-' |
831        '/' |
832        '*' |
833        '%' |
834        '<' |
835        '>' |
836        '[' |
837        ']' |
838        '(' |
839        ')' |
840        '{' |
841        '}' |
842        '^' |
843        '|' |
844        '&' |
845        '~' |
846        '=' |
847        '!' |
848        ':' |
849        ';' |
850        ',' |
851        '?' => Ok(()),
852        _ => Err(WebGLError::InvalidValue),
853    }
854}
855
856fn parse_uniform_name(name: &DOMString) -> Option<(String, Option<i32>)> {
857    let name = name.str();
858    if !name.ends_with(']') {
859        return Some((String::from(name), None));
860    }
861    let bracket_pos = name[..name.len() - 1].rfind('[')?;
862    let index = name[(bracket_pos + 1)..(name.len() - 1)]
863        .parse::<i32>()
864        .ok()?;
865    Some((String::from(&name[..bracket_pos]), Some(index)))
866}
867
868/// <https://registry.khronos.org/webgl/specs/latest/1.0/#6.20>
869/// > WebGL requires support of tokens up to 256 characters in length.
870/// > Shaders containing tokens longer than 256 characters must fail to compile.
871///
872/// FIXME: how is "character" defined here? Should this be `Utf32CodeUnits` instead?
873pub(crate) const MAX_UNIFORM_AND_ATTRIBUTE_LEN: Utf8CodeUnits = Utf8CodeUnits(256);