1use 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 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 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(()), }
306
307 match self.vertex_shader.get() {
308 Some(ref shader) if shader.successfully_compiled() => {},
309 _ => return Ok(()), }
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 if !used_locs.insert(location + column) {
334 return Ok(());
335 }
336 }
337 }
338 for active_uniform in &*link_info.active_uniforms {
339 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 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 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 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 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 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 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 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 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 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;