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