mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 14:02:46 +00:00 
			
		
		
		
	LibWeb: Remove shadow roots from elements that are removed from the DOM
We currently create a shadow tree once for each DOM element that renders with a shadow tree (e.g. <input>, <details>). If such an element is removed from the DOM, we must remove its shadow tree. Otherwise, the shadow tree will refer to the old document in perpetuity. If the node is added back to a DOM, then recreate the shadow tree.
This commit is contained in:
		
							parent
							
								
									3cde479416
								
							
						
					
					
						commit
						ff48b7333c
					
				
					 12 changed files with 67 additions and 3 deletions
				
			
		|  | @ -0,0 +1 @@ | |||
|    <details><summary></summary></details> | ||||
							
								
								
									
										1
									
								
								Tests/LibWeb/Text/expected/HTML/set-innerHTML-input.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Tests/LibWeb/Text/expected/HTML/set-innerHTML-input.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
|    <input> | ||||
|  | @ -0,0 +1 @@ | |||
|    <textarea></textarea> | ||||
							
								
								
									
										9
									
								
								Tests/LibWeb/Text/input/HTML/set-innerHTML-details.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Tests/LibWeb/Text/input/HTML/set-innerHTML-details.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| <div id=test></div> | ||||
| <script src="../include.js"></script> | ||||
| <script> | ||||
|     test(() => { | ||||
|         let element = document.getElementById('test'); | ||||
|         element.innerHTML = "<details><summary></summary></details>"; | ||||
|         println(element.innerHTML); | ||||
|     }); | ||||
| </script> | ||||
							
								
								
									
										9
									
								
								Tests/LibWeb/Text/input/HTML/set-innerHTML-input.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Tests/LibWeb/Text/input/HTML/set-innerHTML-input.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| <div id=test></div> | ||||
| <script src="../include.js"></script> | ||||
| <script> | ||||
|     test(() => { | ||||
|         let element = document.getElementById('test'); | ||||
|         element.innerHTML = "<input>"; | ||||
|         println(element.innerHTML); | ||||
|     }); | ||||
| </script> | ||||
							
								
								
									
										9
									
								
								Tests/LibWeb/Text/input/HTML/set-innerHTML-textarea.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Tests/LibWeb/Text/input/HTML/set-innerHTML-textarea.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| <div id=test></div> | ||||
| <script src="../include.js"></script> | ||||
| <script> | ||||
|     test(() => { | ||||
|         let element = document.getElementById('test'); | ||||
|         element.innerHTML = "<textarea></textarea>"; | ||||
|         println(element.innerHTML); | ||||
|     }); | ||||
| </script> | ||||
|  | @ -39,8 +39,17 @@ void HTMLDetailsElement::initialize(JS::Realm& realm) | |||
| { | ||||
|     Base::initialize(realm); | ||||
|     set_prototype(&Bindings::ensure_web_prototype<Bindings::HTMLDetailsElementPrototype>(realm, "HTMLDetailsElement"_fly_string)); | ||||
| } | ||||
| 
 | ||||
|     create_shadow_tree(realm).release_value_but_fixme_should_propagate_errors(); | ||||
| void HTMLDetailsElement::inserted() | ||||
| { | ||||
|     create_shadow_tree_if_needed().release_value_but_fixme_should_propagate_errors(); | ||||
|     update_shadow_tree_slots(); | ||||
| } | ||||
| 
 | ||||
| void HTMLDetailsElement::removed_from(DOM::Node*) | ||||
| { | ||||
|     set_shadow_root(nullptr); | ||||
| } | ||||
| 
 | ||||
| void HTMLDetailsElement::attribute_changed(FlyString const& name, Optional<String> const& value) | ||||
|  | @ -107,8 +116,13 @@ void HTMLDetailsElement::queue_a_details_toggle_event_task(String old_state, Str | |||
| } | ||||
| 
 | ||||
| // https://html.spec.whatwg.org/#the-details-and-summary-elements
 | ||||
| WebIDL::ExceptionOr<void> HTMLDetailsElement::create_shadow_tree(JS::Realm& realm) | ||||
| WebIDL::ExceptionOr<void> HTMLDetailsElement::create_shadow_tree_if_needed() | ||||
| { | ||||
|     if (shadow_root_internal()) | ||||
|         return {}; | ||||
| 
 | ||||
|     auto& realm = this->realm(); | ||||
| 
 | ||||
|     // The element is also expected to have an internal shadow tree with two slots.
 | ||||
|     auto shadow_root = heap().allocate<DOM::ShadowRoot>(realm, document(), *this, Bindings::ShadowRootMode::Closed); | ||||
|     shadow_root->set_slot_assignment(Bindings::SlotAssignmentMode::Manual); | ||||
|  | @ -130,6 +144,9 @@ WebIDL::ExceptionOr<void> HTMLDetailsElement::create_shadow_tree(JS::Realm& real | |||
| 
 | ||||
| void HTMLDetailsElement::update_shadow_tree_slots() | ||||
| { | ||||
|     if (!shadow_root_internal()) | ||||
|         return; | ||||
| 
 | ||||
|     Vector<HTMLSlotElement::SlottableHandle> summary_assignment; | ||||
|     Vector<HTMLSlotElement::SlottableHandle> descendants_assignment; | ||||
| 
 | ||||
|  | @ -159,6 +176,9 @@ void HTMLDetailsElement::update_shadow_tree_slots() | |||
| // https://html.spec.whatwg.org/#the-details-and-summary-elements:the-details-element-6
 | ||||
| void HTMLDetailsElement::update_shadow_tree_style() | ||||
| { | ||||
|     if (!shadow_root_internal()) | ||||
|         return; | ||||
| 
 | ||||
|     if (has_attribute(HTML::AttributeNames::open)) { | ||||
|         MUST(m_descendants_slot->set_attribute(HTML::AttributeNames::style, R"~~~( | ||||
|             display: block; | ||||
|  |  | |||
|  | @ -31,12 +31,14 @@ private: | |||
|     virtual void initialize(JS::Realm&) override; | ||||
|     virtual void visit_edges(Cell::Visitor&) override; | ||||
| 
 | ||||
|     virtual void inserted() override; | ||||
|     virtual void removed_from(DOM::Node*) override; | ||||
|     virtual void children_changed() override; | ||||
|     virtual void attribute_changed(FlyString const& name, Optional<String> const& value) override; | ||||
| 
 | ||||
|     void queue_a_details_toggle_event_task(String old_state, String new_state); | ||||
| 
 | ||||
|     WebIDL::ExceptionOr<void> create_shadow_tree(JS::Realm&); | ||||
|     WebIDL::ExceptionOr<void> create_shadow_tree_if_needed(); | ||||
|     void update_shadow_tree_slots(); | ||||
|     void update_shadow_tree_style(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1003,6 +1003,11 @@ void HTMLInputElement::form_associated_element_was_inserted() | |||
|     create_shadow_tree_if_needed(); | ||||
| } | ||||
| 
 | ||||
| void HTMLInputElement::form_associated_element_was_removed(DOM::Node*) | ||||
| { | ||||
|     set_shadow_root(nullptr); | ||||
| } | ||||
| 
 | ||||
| // https://html.spec.whatwg.org/multipage/input.html#radio-button-group
 | ||||
| static bool is_in_same_radio_button_group(HTML::HTMLInputElement const& a, HTML::HTMLInputElement const& b) | ||||
| { | ||||
|  |  | |||
|  | @ -139,6 +139,7 @@ public: | |||
|     virtual void reset_algorithm() override; | ||||
| 
 | ||||
|     virtual void form_associated_element_was_inserted() override; | ||||
|     virtual void form_associated_element_was_removed(DOM::Node*) override; | ||||
| 
 | ||||
|     // ^HTMLElement
 | ||||
|     // https://html.spec.whatwg.org/multipage/forms.html#category-label
 | ||||
|  |  | |||
|  | @ -91,6 +91,11 @@ void HTMLTextAreaElement::form_associated_element_was_inserted() | |||
|     create_shadow_tree_if_needed(); | ||||
| } | ||||
| 
 | ||||
| void HTMLTextAreaElement::form_associated_element_was_removed(DOM::Node*) | ||||
| { | ||||
|     set_shadow_root(nullptr); | ||||
| } | ||||
| 
 | ||||
| void HTMLTextAreaElement::create_shadow_tree_if_needed() | ||||
| { | ||||
|     if (shadow_root_internal()) | ||||
|  |  | |||
|  | @ -62,6 +62,7 @@ public: | |||
|     virtual void reset_algorithm() override; | ||||
| 
 | ||||
|     virtual void form_associated_element_was_inserted() override; | ||||
|     virtual void form_associated_element_was_removed(DOM::Node*) override; | ||||
| 
 | ||||
|     virtual void children_changed() override; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy Flynn
						Timothy Flynn