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 location = receiver.recv().unwrap();
579 let context_id = self.upcast().context_id();
580
581 Ok(Some(WebGLUniformLocation::new(
582 cx,
583 self.global().as_window(),
584 location,
585 context_id,
586 self.id(),
587 self.link_generation.get(),
588 size,
589 type_,
590 )))
591 }
592
593 pub(crate) fn get_uniform_block_index(&self, name: DOMString) -> WebGLResult<u32> {
594 if !self.link_called.get() || self.is_deleted() {
595 return Err(WebGLError::InvalidOperation);
596 }
597
598 if !validate_glsl_name(&name)? {
599 return Ok(constants2::INVALID_INDEX);
600 }
601
602 let (sender, receiver) = webgl_channel().unwrap();
603 self.upcast()
604 .send_command(WebGLCommand::GetUniformBlockIndex(
605 self.id(),
606 name.into(),
607 sender,
608 ));
609 Ok(receiver.recv().unwrap())
610 }
611
612 pub(crate) fn get_uniform_indices(&self, names: Vec<DOMString>) -> WebGLResult<Vec<u32>> {
613 if !self.link_called.get() || self.is_deleted() {
614 return Err(WebGLError::InvalidOperation);
615 }
616
617 let validation_errors = names.iter().map(validate_glsl_name).collect::<Vec<_>>();
618 let first_validation_error = validation_errors.iter().find(|result| result.is_err());
619 if let Some(error) = first_validation_error {
620 return Err(error.unwrap_err());
621 }
622
623 let names = names
624 .iter()
625 .map(|name| name.to_string())
626 .collect::<Vec<_>>();
627
628 let (sender, receiver) = webgl_channel().unwrap();
629 self.upcast()
630 .send_command(WebGLCommand::GetUniformIndices(self.id(), names, sender));
631 Ok(receiver.recv().unwrap())
632 }
633
634 pub(crate) fn get_active_uniforms(
635 &self,
636 indices: Vec<u32>,
637 pname: u32,
638 ) -> WebGLResult<Vec<i32>> {
639 if !self.is_linked() || self.is_deleted() {
640 return Err(WebGLError::InvalidOperation);
641 }
642
643 match pname {
644 constants2::UNIFORM_TYPE |
645 constants2::UNIFORM_SIZE |
646 constants2::UNIFORM_BLOCK_INDEX |
647 constants2::UNIFORM_OFFSET |
648 constants2::UNIFORM_ARRAY_STRIDE |
649 constants2::UNIFORM_MATRIX_STRIDE |
650 constants2::UNIFORM_IS_ROW_MAJOR => {},
651 _ => return Err(WebGLError::InvalidEnum),
652 }
653
654 if indices.len() > self.active_uniforms.borrow().len() {
655 return Err(WebGLError::InvalidValue);
656 }
657
658 let (sender, receiver) = webgl_channel().unwrap();
659 self.upcast().send_command(WebGLCommand::GetActiveUniforms(
660 self.id(),
661 indices,
662 pname,
663 sender,
664 ));
665 Ok(receiver.recv().unwrap())
666 }
667
668 pub(crate) fn get_active_uniform_block_parameter(
669 &self,
670 block_index: u32,
671 pname: u32,
672 ) -> WebGLResult<Vec<i32>> {
673 if !self.link_called.get() || self.is_deleted() {
674 return Err(WebGLError::InvalidOperation);
675 }
676
677 if block_index as usize >= self.active_uniform_blocks.borrow().len() {
678 return Err(WebGLError::InvalidValue);
679 }
680
681 match pname {
682 constants2::UNIFORM_BLOCK_BINDING |
683 constants2::UNIFORM_BLOCK_DATA_SIZE |
684 constants2::UNIFORM_BLOCK_ACTIVE_UNIFORMS |
685 constants2::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES |
686 constants2::UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER |
687 constants2::UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER => {},
688 _ => return Err(WebGLError::InvalidEnum),
689 }
690
691 let (sender, receiver) = webgl_channel().unwrap();
692 self.upcast()
693 .send_command(WebGLCommand::GetActiveUniformBlockParameter(
694 self.id(),
695 block_index,
696 pname,
697 sender,
698 ));
699 Ok(receiver.recv().unwrap())
700 }
701
702 pub(crate) fn get_active_uniform_block_name(&self, block_index: u32) -> WebGLResult<String> {
703 if !self.link_called.get() || self.is_deleted() {
704 return Err(WebGLError::InvalidOperation);
705 }
706
707 if block_index as usize >= self.active_uniform_blocks.borrow().len() {
708 return Err(WebGLError::InvalidValue);
709 }
710
711 let (sender, receiver) = webgl_channel().unwrap();
712 self.upcast()
713 .send_command(WebGLCommand::GetActiveUniformBlockName(
714 self.id(),
715 block_index,
716 sender,
717 ));
718 Ok(receiver.recv().unwrap())
719 }
720
721 pub(crate) fn bind_uniform_block(
722 &self,
723 block_index: u32,
724 block_binding: u32,
725 ) -> WebGLResult<()> {
726 if block_index as usize >= self.active_uniform_blocks.borrow().len() {
727 return Err(WebGLError::InvalidValue);
728 }
729
730 let mut active_uniforms = self.active_uniforms.borrow_mut();
731 if active_uniforms.len() > block_binding as usize {
732 active_uniforms[block_binding as usize].bind_index = Some(block_binding);
733 }
734
735 self.upcast()
736 .send_command(WebGLCommand::UniformBlockBinding(
737 self.id(),
738 block_index,
739 block_binding,
740 ));
741 Ok(())
742 }
743
744 pub(crate) fn get_info_log(&self) -> WebGLResult<String> {
746 if self.is_deleted() {
747 return Err(WebGLError::InvalidValue);
748 }
749 if self.link_called.get() {
750 let shaders_compiled = match (self.fragment_shader.get(), self.vertex_shader.get()) {
751 (Some(fs), Some(vs)) => fs.successfully_compiled() && vs.successfully_compiled(),
752 _ => false,
753 };
754 if !shaders_compiled {
755 return Ok("One or more shaders failed to compile".to_string());
756 }
757 }
758 let (sender, receiver) = webgl_channel().unwrap();
759 self.upcast()
760 .send_command(WebGLCommand::GetProgramInfoLog(self.id(), sender));
761 Ok(receiver.recv().unwrap())
762 }
763
764 pub(crate) fn attached_shaders(&self) -> WebGLResult<Vec<DomRoot<WebGLShader>>> {
765 if self.is_marked_for_deletion() {
766 return Err(WebGLError::InvalidValue);
767 }
768 Ok(
769 match (self.vertex_shader.get(), self.fragment_shader.get()) {
770 (Some(vertex_shader), Some(fragment_shader)) => {
771 vec![vertex_shader, fragment_shader]
772 },
773 (Some(shader), None) | (None, Some(shader)) => vec![shader],
774 (None, None) => vec![],
775 },
776 )
777 }
778
779 pub(crate) fn link_generation(&self) -> u64 {
780 self.link_generation.get()
781 }
782
783 pub(crate) fn transform_feedback_varyings_length(&self) -> i32 {
784 self.transform_feedback_varyings_length.get()
785 }
786
787 pub(crate) fn transform_feedback_buffer_mode(&self) -> i32 {
788 self.transform_feedback_mode.get()
789 }
790
791 fn set_marked_for_deletion(&self, value: bool) {
792 self.droppable.borrow_mut().marked_for_deletion = value
793 }
794
795 fn set_is_in_use(&self, value: bool) {
796 self.droppable.borrow_mut().is_in_use = value
797 }
798}
799
800fn validate_glsl_name(name: &DOMString) -> WebGLResult<bool> {
801 if name.is_empty() {
802 return Ok(false);
803 }
804 if name.len_utf8() > MAX_UNIFORM_AND_ATTRIBUTE_LEN {
805 return Err(WebGLError::InvalidValue);
806 }
807 for c in name.str().chars() {
808 validate_glsl_char(c)?;
809 }
810 if name.starts_with_str("webgl_") || name.starts_with_str("_webgl_") {
811 return Err(WebGLError::InvalidOperation);
812 }
813 Ok(true)
814}
815
816fn validate_glsl_char(c: char) -> WebGLResult<()> {
817 match c {
818 'a'..='z' |
819 'A'..='Z' |
820 '0'..='9' |
821 ' ' |
822 '\t' |
823 '\u{11}' |
824 '\u{12}' |
825 '\r' |
826 '\n' |
827 '_' |
828 '.' |
829 '+' |
830 '-' |
831 '/' |
832 '*' |
833 '%' |
834 '<' |
835 '>' |
836 '[' |
837 ']' |
838 '(' |
839 ')' |
840 '{' |
841 '}' |
842 '^' |
843 '|' |
844 '&' |
845 '~' |
846 '=' |
847 '!' |
848 ':' |
849 ';' |
850 ',' |
851 '?' => Ok(()),
852 _ => Err(WebGLError::InvalidValue),
853 }
854}
855
856fn parse_uniform_name(name: &DOMString) -> Option<(String, Option<i32>)> {
857 let name = name.str();
858 if !name.ends_with(']') {
859 return Some((String::from(name), None));
860 }
861 let bracket_pos = name[..name.len() - 1].rfind('[')?;
862 let index = name[(bracket_pos + 1)..(name.len() - 1)]
863 .parse::<i32>()
864 .ok()?;
865 Some((String::from(&name[..bracket_pos]), Some(index)))
866}
867
868pub(crate) const MAX_UNIFORM_AND_ATTRIBUTE_LEN: Utf8CodeUnits = Utf8CodeUnits(256);