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