1use std::cell::Cell;
6use std::default::Default;
7use std::ops::Range;
8
9use dom_struct::dom_struct;
10use html5ever::{LocalName, Prefix, local_name, ns};
11use js::rust::HandleObject;
12use style::attr::AttrValue;
13use stylo_dom::ElementState;
14
15use crate::clipboard_provider::EmbedderClipboardProvider;
16use crate::dom::attr::Attr;
17use crate::dom::bindings::cell::DomRefCell;
18use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
19use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::SelectionMode;
20use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
21use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
22use crate::dom::bindings::error::ErrorResult;
23use crate::dom::bindings::inheritance::Castable;
24use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
25use crate::dom::bindings::str::DOMString;
26use crate::dom::clipboardevent::ClipboardEvent;
27use crate::dom::compositionevent::CompositionEvent;
28use crate::dom::document::Document;
29use crate::dom::element::{AttributeMutation, Element, LayoutElementHelpers};
30use crate::dom::event::{Event, EventBubbles, EventCancelable};
31use crate::dom::html::htmlelement::HTMLElement;
32use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
33use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
34use crate::dom::html::htmlinputelement::HTMLInputElement;
35use crate::dom::keyboardevent::KeyboardEvent;
36use crate::dom::node::{
37 BindContext, ChildrenMutation, CloneChildrenFlag, Node, NodeDamage, NodeTraits, UnbindContext,
38};
39use crate::dom::nodelist::NodeList;
40use crate::dom::textcontrol::{TextControlElement, TextControlSelection};
41use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
42use crate::dom::validitystate::{ValidationFlags, ValidityState};
43use crate::dom::virtualmethods::VirtualMethods;
44use crate::script_runtime::CanGc;
45use crate::textinput::{
46 ClipboardEventReaction, Direction, KeyReaction, Lines, SelectionDirection, TextInput,
47 UTF8Bytes, UTF16CodeUnits,
48};
49
50#[dom_struct]
51pub(crate) struct HTMLTextAreaElement {
52 htmlelement: HTMLElement,
53 #[no_trace]
54 textinput: DomRefCell<TextInput<EmbedderClipboardProvider>>,
55 placeholder: DomRefCell<DOMString>,
56 value_dirty: Cell<bool>,
58 form_owner: MutNullableDom<HTMLFormElement>,
59 labels_node_list: MutNullableDom<NodeList>,
60 validity_state: MutNullableDom<ValidityState>,
61}
62
63pub(crate) trait LayoutHTMLTextAreaElementHelpers {
64 fn value_for_layout(self) -> String;
65 fn selection_for_layout(self) -> Option<Range<usize>>;
66 fn get_cols(self) -> u32;
67 fn get_rows(self) -> u32;
68}
69
70#[allow(unsafe_code)]
71impl<'dom> LayoutDom<'dom, HTMLTextAreaElement> {
72 fn textinput_content(self) -> DOMString {
73 unsafe {
74 self.unsafe_get()
75 .textinput
76 .borrow_for_layout()
77 .get_content()
78 }
79 }
80
81 fn textinput_sorted_selection_offsets_range(self) -> Range<UTF8Bytes> {
82 unsafe {
83 self.unsafe_get()
84 .textinput
85 .borrow_for_layout()
86 .sorted_selection_offsets_range()
87 }
88 }
89
90 fn placeholder(self) -> &'dom str {
91 unsafe { self.unsafe_get().placeholder.borrow_for_layout() }
92 }
93}
94
95impl LayoutHTMLTextAreaElementHelpers for LayoutDom<'_, HTMLTextAreaElement> {
96 fn value_for_layout(self) -> String {
97 let text = self.textinput_content();
98 if text.is_empty() {
99 self.placeholder().replace("\r\n", "\n").replace('\r', "\n")
102 } else {
103 text.into()
104 }
105 }
106
107 fn selection_for_layout(self) -> Option<Range<usize>> {
108 if !self.upcast::<Element>().focus_state() {
109 return None;
110 }
111 Some(UTF8Bytes::unwrap_range(
112 self.textinput_sorted_selection_offsets_range(),
113 ))
114 }
115
116 fn get_cols(self) -> u32 {
117 self.upcast::<Element>()
118 .get_attr_for_layout(&ns!(), &local_name!("cols"))
119 .map_or(DEFAULT_COLS, AttrValue::as_uint)
120 }
121
122 fn get_rows(self) -> u32 {
123 self.upcast::<Element>()
124 .get_attr_for_layout(&ns!(), &local_name!("rows"))
125 .map_or(DEFAULT_ROWS, AttrValue::as_uint)
126 }
127}
128
129const DEFAULT_COLS: u32 = 20;
131
132const DEFAULT_ROWS: u32 = 2;
134
135const DEFAULT_MAX_LENGTH: i32 = -1;
136const DEFAULT_MIN_LENGTH: i32 = -1;
137
138impl HTMLTextAreaElement {
139 fn new_inherited(
140 local_name: LocalName,
141 prefix: Option<Prefix>,
142 document: &Document,
143 ) -> HTMLTextAreaElement {
144 let constellation_sender = document
145 .window()
146 .as_global_scope()
147 .script_to_constellation_chan()
148 .clone();
149 HTMLTextAreaElement {
150 htmlelement: HTMLElement::new_inherited_with_state(
151 ElementState::ENABLED | ElementState::READWRITE,
152 local_name,
153 prefix,
154 document,
155 ),
156 placeholder: DomRefCell::new(DOMString::new()),
157 textinput: DomRefCell::new(TextInput::new(
158 Lines::Multiple,
159 DOMString::new(),
160 EmbedderClipboardProvider {
161 constellation_sender,
162 webview_id: document.webview_id(),
163 },
164 None,
165 None,
166 SelectionDirection::None,
167 )),
168 value_dirty: Cell::new(false),
169 form_owner: Default::default(),
170 labels_node_list: Default::default(),
171 validity_state: Default::default(),
172 }
173 }
174
175 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
176 pub(crate) fn new(
177 local_name: LocalName,
178 prefix: Option<Prefix>,
179 document: &Document,
180 proto: Option<HandleObject>,
181 can_gc: CanGc,
182 ) -> DomRoot<HTMLTextAreaElement> {
183 Node::reflect_node_with_proto(
184 Box::new(HTMLTextAreaElement::new_inherited(
185 local_name, prefix, document,
186 )),
187 document,
188 proto,
189 can_gc,
190 )
191 }
192
193 pub(crate) fn auto_directionality(&self) -> String {
194 let value: String = self.Value().to_string();
195 HTMLInputElement::directionality_from_value(&value)
196 }
197
198 fn update_placeholder_shown_state(&self) {
199 let has_placeholder = !self.placeholder.borrow().is_empty();
200 let has_value = !self.textinput.borrow().is_empty();
201 let el = self.upcast::<Element>();
202 el.set_placeholder_shown_state(has_placeholder && !has_value);
203 }
204
205 pub(crate) fn is_mutable(&self) -> bool {
207 !(self.upcast::<Element>().disabled_state() || self.ReadOnly())
210 }
211}
212
213impl TextControlElement for HTMLTextAreaElement {
214 fn selection_api_applies(&self) -> bool {
215 true
216 }
217
218 fn has_selectable_text(&self) -> bool {
219 true
220 }
221
222 fn set_dirty_value_flag(&self, value: bool) {
223 self.value_dirty.set(value)
224 }
225}
226
227impl HTMLTextAreaElementMethods<crate::DomTypeHolder> for HTMLTextAreaElement {
228 make_uint_getter!(Cols, "cols", DEFAULT_COLS);
233
234 make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS);
236
237 make_getter!(DirName, "dirname");
239
240 make_setter!(SetDirName, "dirname");
242
243 make_bool_getter!(Disabled, "disabled");
245
246 make_bool_setter!(SetDisabled, "disabled");
248
249 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
251 self.form_owner()
252 }
253
254 make_getter!(Name, "name");
256
257 make_atomic_setter!(SetName, "name");
259
260 make_getter!(Placeholder, "placeholder");
262
263 make_setter!(SetPlaceholder, "placeholder");
265
266 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
268
269 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
271
272 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
274
275 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
277
278 make_bool_getter!(ReadOnly, "readonly");
280
281 make_bool_setter!(SetReadOnly, "readonly");
283
284 make_bool_getter!(Required, "required");
286
287 make_bool_setter!(SetRequired, "required");
289
290 make_uint_getter!(Rows, "rows", DEFAULT_ROWS);
292
293 make_limited_uint_setter!(SetRows, "rows", DEFAULT_ROWS);
295
296 make_getter!(Wrap, "wrap");
298
299 make_setter!(SetWrap, "wrap");
301
302 fn Type(&self) -> DOMString {
304 DOMString::from("textarea")
305 }
306
307 fn DefaultValue(&self) -> DOMString {
309 self.upcast::<Node>().GetTextContent().unwrap()
310 }
311
312 fn SetDefaultValue(&self, value: DOMString, can_gc: CanGc) {
314 self.upcast::<Node>()
315 .set_text_content_for_element(Some(value), can_gc);
316
317 if !self.value_dirty.get() {
320 self.reset();
321 }
322 }
323
324 fn Value(&self) -> DOMString {
326 self.textinput.borrow().get_content()
327 }
328
329 fn SetValue(&self, value: DOMString) {
331 {
332 let mut textinput = self.textinput.borrow_mut();
333
334 let old_value = textinput.get_content();
336
337 textinput.set_content(value);
339
340 self.value_dirty.set(true);
342
343 if old_value != textinput.get_content() {
344 textinput.clear_selection_to_limit(Direction::Forward);
346 }
347 }
348
349 self.validity_state()
350 .perform_validation_and_update(ValidationFlags::all(), CanGc::note());
351 self.upcast::<Node>().dirty(NodeDamage::Other);
352 }
353
354 fn TextLength(&self) -> u32 {
356 let UTF16CodeUnits(num_units) = self.textinput.borrow().utf16_len();
357 num_units as u32
358 }
359
360 make_labels_getter!(Labels, labels_node_list);
362
363 fn Select(&self) {
365 self.selection().dom_select();
366 }
367
368 fn GetSelectionStart(&self) -> Option<u32> {
370 self.selection().dom_start()
371 }
372
373 fn SetSelectionStart(&self, start: Option<u32>) -> ErrorResult {
375 self.selection().set_dom_start(start)
376 }
377
378 fn GetSelectionEnd(&self) -> Option<u32> {
380 self.selection().dom_end()
381 }
382
383 fn SetSelectionEnd(&self, end: Option<u32>) -> ErrorResult {
385 self.selection().set_dom_end(end)
386 }
387
388 fn GetSelectionDirection(&self) -> Option<DOMString> {
390 self.selection().dom_direction()
391 }
392
393 fn SetSelectionDirection(&self, direction: Option<DOMString>) -> ErrorResult {
395 self.selection().set_dom_direction(direction)
396 }
397
398 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
400 self.selection().set_dom_range(start, end, direction)
401 }
402
403 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
405 self.selection()
406 .set_dom_range_text(replacement, None, None, Default::default())
407 }
408
409 fn SetRangeText_(
411 &self,
412 replacement: DOMString,
413 start: u32,
414 end: u32,
415 selection_mode: SelectionMode,
416 ) -> ErrorResult {
417 self.selection()
418 .set_dom_range_text(replacement, Some(start), Some(end), selection_mode)
419 }
420
421 fn WillValidate(&self) -> bool {
423 self.is_instance_validatable()
424 }
425
426 fn Validity(&self) -> DomRoot<ValidityState> {
428 self.validity_state()
429 }
430
431 fn CheckValidity(&self, can_gc: CanGc) -> bool {
433 self.check_validity(can_gc)
434 }
435
436 fn ReportValidity(&self, can_gc: CanGc) -> bool {
438 self.report_validity(can_gc)
439 }
440
441 fn ValidationMessage(&self) -> DOMString {
443 self.validation_message()
444 }
445
446 fn SetCustomValidity(&self, error: DOMString) {
448 self.validity_state().set_custom_error_message(error);
449 }
450}
451
452impl HTMLTextAreaElement {
453 pub(crate) fn clear(&self) {
456 self.value_dirty.set(false);
457 self.textinput.borrow_mut().set_content(DOMString::from(""));
458 }
459
460 pub(crate) fn reset(&self) {
461 let mut textinput = self.textinput.borrow_mut();
463 textinput.set_content(self.DefaultValue());
464 self.value_dirty.set(false);
465 }
466
467 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
468 fn selection(&self) -> TextControlSelection<'_, Self> {
469 TextControlSelection::new(self, &self.textinput)
470 }
471}
472
473impl VirtualMethods for HTMLTextAreaElement {
474 fn super_type(&self) -> Option<&dyn VirtualMethods> {
475 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
476 }
477
478 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
479 self.super_type()
480 .unwrap()
481 .attribute_mutated(attr, mutation, can_gc);
482 match *attr.local_name() {
483 local_name!("disabled") => {
484 let el = self.upcast::<Element>();
485 match mutation {
486 AttributeMutation::Set(_) => {
487 el.set_disabled_state(true);
488 el.set_enabled_state(false);
489
490 el.set_read_write_state(false);
491 },
492 AttributeMutation::Removed => {
493 el.set_disabled_state(false);
494 el.set_enabled_state(true);
495 el.check_ancestors_disabled_state_for_form_control();
496
497 if !el.disabled_state() && !el.read_write_state() {
498 el.set_read_write_state(true);
499 }
500 },
501 }
502 el.update_sequentially_focusable_status(CanGc::note());
503 },
504 local_name!("maxlength") => match *attr.value() {
505 AttrValue::Int(_, value) => {
506 let mut textinput = self.textinput.borrow_mut();
507
508 if value < 0 {
509 textinput.set_max_length(None);
510 } else {
511 textinput.set_max_length(Some(UTF16CodeUnits(value as usize)))
512 }
513 },
514 _ => panic!("Expected an AttrValue::Int"),
515 },
516 local_name!("minlength") => match *attr.value() {
517 AttrValue::Int(_, value) => {
518 let mut textinput = self.textinput.borrow_mut();
519
520 if value < 0 {
521 textinput.set_min_length(None);
522 } else {
523 textinput.set_min_length(Some(UTF16CodeUnits(value as usize)))
524 }
525 },
526 _ => panic!("Expected an AttrValue::Int"),
527 },
528 local_name!("placeholder") => {
529 {
530 let mut placeholder = self.placeholder.borrow_mut();
531 placeholder.clear();
532 if let AttributeMutation::Set(_) = mutation {
533 placeholder.push_str(&attr.value());
534 }
535 }
536 self.update_placeholder_shown_state();
537 },
538 local_name!("readonly") => {
539 let el = self.upcast::<Element>();
540 match mutation {
541 AttributeMutation::Set(_) => {
542 el.set_read_write_state(false);
543 },
544 AttributeMutation::Removed => {
545 el.set_read_write_state(!el.disabled_state());
546 },
547 }
548 },
549 local_name!("form") => {
550 self.form_attribute_mutated(mutation, can_gc);
551 },
552 _ => {},
553 }
554
555 self.validity_state()
556 .perform_validation_and_update(ValidationFlags::all(), can_gc);
557 }
558
559 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
560 if let Some(s) = self.super_type() {
561 s.bind_to_tree(context, can_gc);
562 }
563
564 self.upcast::<Element>()
565 .check_ancestors_disabled_state_for_form_control();
566
567 self.validity_state()
568 .perform_validation_and_update(ValidationFlags::all(), can_gc);
569 }
570
571 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
572 match *name {
573 local_name!("cols") => AttrValue::from_limited_u32(value.into(), DEFAULT_COLS),
574 local_name!("rows") => AttrValue::from_limited_u32(value.into(), DEFAULT_ROWS),
575 local_name!("maxlength") => {
576 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
577 },
578 local_name!("minlength") => {
579 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
580 },
581 _ => self
582 .super_type()
583 .unwrap()
584 .parse_plain_attribute(name, value),
585 }
586 }
587
588 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
589 self.super_type().unwrap().unbind_from_tree(context, can_gc);
590
591 let node = self.upcast::<Node>();
592 let el = self.upcast::<Element>();
593 if node
594 .ancestors()
595 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
596 {
597 el.check_ancestors_disabled_state_for_form_control();
598 } else {
599 el.check_disabled_attribute();
600 }
601
602 self.validity_state()
603 .perform_validation_and_update(ValidationFlags::all(), can_gc);
604 }
605
606 fn cloning_steps(
609 &self,
610 copy: &Node,
611 maybe_doc: Option<&Document>,
612 clone_children: CloneChildrenFlag,
613 can_gc: CanGc,
614 ) {
615 if let Some(s) = self.super_type() {
616 s.cloning_steps(copy, maybe_doc, clone_children, can_gc);
617 }
618 let el = copy.downcast::<HTMLTextAreaElement>().unwrap();
619 el.value_dirty.set(self.value_dirty.get());
620 {
621 let mut textinput = el.textinput.borrow_mut();
622 textinput.set_content(self.textinput.borrow().get_content());
623 }
624 el.validity_state()
625 .perform_validation_and_update(ValidationFlags::all(), can_gc);
626 }
627
628 fn children_changed(&self, mutation: &ChildrenMutation) {
629 if let Some(s) = self.super_type() {
630 s.children_changed(mutation);
631 }
632 if !self.value_dirty.get() {
633 self.reset();
634 }
635 }
636
637 fn handle_event(&self, event: &Event, can_gc: CanGc) {
639 if let Some(s) = self.super_type() {
640 s.handle_event(event, can_gc);
641 }
642
643 if event.type_() == atom!("click") && !event.DefaultPrevented() {
644 } else if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
646 if let Some(kevent) = event.downcast::<KeyboardEvent>() {
647 let action = self.textinput.borrow_mut().handle_keydown(kevent);
650 match action {
651 KeyReaction::TriggerDefaultAction => (),
652 KeyReaction::DispatchInput => {
653 if event.IsTrusted() {
654 self.owner_global()
655 .task_manager()
656 .user_interaction_task_source()
657 .queue_event(
658 self.upcast(),
659 atom!("input"),
660 EventBubbles::Bubbles,
661 EventCancelable::NotCancelable,
662 );
663 }
664 self.value_dirty.set(true);
665 self.update_placeholder_shown_state();
666 self.upcast::<Node>().dirty(NodeDamage::Other);
667 event.mark_as_handled();
668 },
669 KeyReaction::RedrawSelection => {
670 self.upcast::<Node>().dirty(NodeDamage::Other);
671 event.mark_as_handled();
672 },
673 KeyReaction::Nothing => (),
674 }
675 }
676 } else if event.type_() == atom!("keypress") && !event.DefaultPrevented() {
677 } else if event.type_() == atom!("compositionstart") ||
681 event.type_() == atom!("compositionupdate") ||
682 event.type_() == atom!("compositionend")
683 {
684 if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
685 if event.type_() == atom!("compositionend") {
686 let _ = self
687 .textinput
688 .borrow_mut()
689 .handle_compositionend(compositionevent);
690 self.upcast::<Node>().dirty(NodeDamage::Other);
691 } else if event.type_() == atom!("compositionupdate") {
692 let _ = self
693 .textinput
694 .borrow_mut()
695 .handle_compositionupdate(compositionevent);
696 self.upcast::<Node>().dirty(NodeDamage::Other);
697 }
698 event.mark_as_handled();
699 }
700 } else if let Some(clipboard_event) = event.downcast::<ClipboardEvent>() {
701 let reaction = self
702 .textinput
703 .borrow_mut()
704 .handle_clipboard_event(clipboard_event);
705 if reaction.contains(ClipboardEventReaction::FireClipboardChangedEvent) {
706 self.owner_document()
707 .event_handler()
708 .fire_clipboardchange_event(can_gc);
709 }
710 if reaction.contains(ClipboardEventReaction::QueueInputEvent) {
711 self.owner_global()
712 .task_manager()
713 .user_interaction_task_source()
714 .queue_event(
715 self.upcast(),
716 atom!("input"),
717 EventBubbles::Bubbles,
718 EventCancelable::NotCancelable,
719 );
720 }
721 if !reaction.is_empty() {
722 self.upcast::<Node>().dirty(NodeDamage::ContentOrHeritage);
723 }
724 }
725
726 self.validity_state()
727 .perform_validation_and_update(ValidationFlags::all(), can_gc);
728 }
729
730 fn pop(&self) {
731 self.super_type().unwrap().pop();
732
733 self.reset();
735 }
736}
737
738impl FormControl for HTMLTextAreaElement {
739 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
740 self.form_owner.get()
741 }
742
743 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
744 self.form_owner.set(form);
745 }
746
747 fn to_element(&self) -> &Element {
748 self.upcast::<Element>()
749 }
750}
751
752impl Validatable for HTMLTextAreaElement {
753 fn as_element(&self) -> &Element {
754 self.upcast()
755 }
756
757 fn validity_state(&self) -> DomRoot<ValidityState> {
758 self.validity_state
759 .or_init(|| ValidityState::new(&self.owner_window(), self.upcast(), CanGc::note()))
760 }
761
762 fn is_instance_validatable(&self) -> bool {
763 !self.upcast::<Element>().disabled_state() &&
767 !self.ReadOnly() &&
768 !is_barred_by_datalist_ancestor(self.upcast())
769 }
770
771 fn perform_validation(
772 &self,
773 validate_flags: ValidationFlags,
774 _can_gc: CanGc,
775 ) -> ValidationFlags {
776 let mut failed_flags = ValidationFlags::empty();
777
778 let textinput = self.textinput.borrow();
779 let UTF16CodeUnits(value_len) = textinput.utf16_len();
780 let last_edit_by_user = !textinput.was_last_change_by_set_content();
781 let value_dirty = self.value_dirty.get();
782
783 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
786 self.Required() &&
787 self.is_mutable() &&
788 value_len == 0
789 {
790 failed_flags.insert(ValidationFlags::VALUE_MISSING);
791 }
792
793 if value_dirty && last_edit_by_user && value_len > 0 {
794 if validate_flags.contains(ValidationFlags::TOO_LONG) {
797 let max_length = self.MaxLength();
798 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as usize) {
799 failed_flags.insert(ValidationFlags::TOO_LONG);
800 }
801 }
802
803 if validate_flags.contains(ValidationFlags::TOO_SHORT) {
806 let min_length = self.MinLength();
807 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as usize) {
808 failed_flags.insert(ValidationFlags::TOO_SHORT);
809 }
810 }
811 }
812
813 failed_flags
814 }
815}