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