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