mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 19:22:45 +00:00 
			
		
		
		
	LibWeb: Check for valid names in Document.createElement() & friends
We now validate that the provided tag names are valid XML tag names, and otherwise throw an "invalid character" DOM exception. 2% progression on ACID3. :^)
This commit is contained in:
		
							parent
							
								
									8daf603f46
								
							
						
					
					
						commit
						fe67fe3791
					
				
					 7 changed files with 82 additions and 24 deletions
				
			
		|  | @ -59,11 +59,11 @@ void EvaluateExpressionDialog::build(Window* parent_window) | |||
| 
 | ||||
|     auto base_document = Web::DOM::Document::create(); | ||||
|     base_document->append_child(adopt_ref(*new Web::DOM::DocumentType(base_document))); | ||||
|     auto html_element = base_document->create_element("html"); | ||||
|     auto html_element = base_document->create_element("html").release_value(); | ||||
|     base_document->append_child(html_element); | ||||
|     auto head_element = base_document->create_element("head"); | ||||
|     auto head_element = base_document->create_element("head").release_value(); | ||||
|     html_element->append_child(head_element); | ||||
|     auto body_element = base_document->create_element("body"); | ||||
|     auto body_element = base_document->create_element("body").release_value(); | ||||
|     html_element->append_child(body_element); | ||||
|     m_output_container = body_element; | ||||
| 
 | ||||
|  | @ -138,7 +138,7 @@ void EvaluateExpressionDialog::handle_evaluation(const String& expression) | |||
| 
 | ||||
| void EvaluateExpressionDialog::set_output(StringView html) | ||||
| { | ||||
|     auto paragraph = m_output_container->document().create_element("p"); | ||||
|     auto paragraph = m_output_container->document().create_element("p").release_value(); | ||||
|     paragraph->set_inner_html(html); | ||||
| 
 | ||||
|     m_output_container->append_child(paragraph); | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ DOMImplementation::DOMImplementation(Document& document) | |||
| } | ||||
| 
 | ||||
| // https://dom.spec.whatwg.org/#dom-domimplementation-createdocument
 | ||||
| NonnullRefPtr<Document> DOMImplementation::create_document(const String& namespace_, const String& qualified_name) const | ||||
| ExceptionOr<NonnullRefPtr<Document>> DOMImplementation::create_document(const String& namespace_, const String& qualified_name) const | ||||
| { | ||||
|     // FIXME: This should specifically be an XML document.
 | ||||
|     auto xml_document = Document::create(); | ||||
|  | @ -29,8 +29,12 @@ NonnullRefPtr<Document> DOMImplementation::create_document(const String& namespa | |||
| 
 | ||||
|     RefPtr<Element> element; | ||||
| 
 | ||||
|     if (!qualified_name.is_empty()) | ||||
|         element = xml_document->create_element_ns(namespace_, qualified_name /* FIXME: and an empty dictionary */); | ||||
|     if (!qualified_name.is_empty()) { | ||||
|         auto new_element = xml_document->create_element_ns(namespace_, qualified_name /* FIXME: and an empty dictionary */); | ||||
|         if (new_element.is_exception()) | ||||
|             return new_element.exception(); | ||||
|         element = new_element.release_value(); | ||||
|     } | ||||
| 
 | ||||
|     // FIXME: If doctype is non-null, append doctype to document.
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ public: | |||
|     } | ||||
| 
 | ||||
|     // FIXME: Add optional DocumentType once supported by IDL
 | ||||
|     NonnullRefPtr<Document> create_document(const String&, const String&) const; | ||||
|     ExceptionOr<NonnullRefPtr<Document>> create_document(const String&, const String&) const; | ||||
|     NonnullRefPtr<Document> create_html_document(const String& title) const; | ||||
|     NonnullRefPtr<DocumentType> create_document_type(String const& qualified_name, String const& public_id, String const& system_id); | ||||
| 
 | ||||
|  |  | |||
|  | @ -444,7 +444,7 @@ void Document::set_title(const String& title) | |||
| 
 | ||||
|     RefPtr<HTML::HTMLTitleElement> title_element = head_element->first_child_of_type<HTML::HTMLTitleElement>(); | ||||
|     if (!title_element) { | ||||
|         title_element = static_ptr_cast<HTML::HTMLTitleElement>(create_element(HTML::TagNames::title)); | ||||
|         title_element = static_ptr_cast<HTML::HTMLTitleElement>(create_element(HTML::TagNames::title).release_value()); | ||||
|         head_element->append_child(*title_element); | ||||
|     } | ||||
| 
 | ||||
|  | @ -864,15 +864,18 @@ JS::Value Document::run_javascript(StringView source, StringView filename) | |||
| 
 | ||||
| // https://dom.spec.whatwg.org/#dom-document-createelement
 | ||||
| // FIXME: This only implements step 6 of the algorithm and does not take in options.
 | ||||
| NonnullRefPtr<Element> Document::create_element(const String& tag_name) | ||||
| DOM::ExceptionOr<NonnullRefPtr<Element>> Document::create_element(String const& tag_name) | ||||
| { | ||||
|     if (!is_valid_name(tag_name)) | ||||
|         return DOM::InvalidCharacterError::create("Invalid character in tag name."); | ||||
| 
 | ||||
|     // FIXME: Let namespace be the HTML namespace, if this is an HTML document or this’s content type is "application/xhtml+xml", and null otherwise.
 | ||||
|     return DOM::create_element(*this, tag_name, Namespace::HTML); | ||||
| } | ||||
| 
 | ||||
| // https://dom.spec.whatwg.org/#internal-createelementns-steps
 | ||||
| // FIXME: This only implements step 4 of the algorithm and does not take in options.
 | ||||
| NonnullRefPtr<Element> Document::create_element_ns(const String& namespace_, const String& qualified_name) | ||||
| DOM::ExceptionOr<NonnullRefPtr<Element>> Document::create_element_ns(const String& namespace_, const String& qualified_name) | ||||
| { | ||||
|     return DOM::create_element(*this, qualified_name, namespace_); | ||||
| } | ||||
|  | @ -1338,4 +1341,53 @@ void Document::detach_parser(Badge<HTML::HTMLParser>) | |||
|     m_parser = nullptr; | ||||
| } | ||||
| 
 | ||||
| // https://www.w3.org/TR/xml/#NT-NameStartChar
 | ||||
| static bool is_valid_name_start_character(u32 code_point) | ||||
| { | ||||
|     return code_point == ':' | ||||
|         || (code_point >= 'A' && code_point <= 'Z') | ||||
|         || code_point == '_' | ||||
|         || (code_point >= 'a' && code_point <= 'z') | ||||
|         || (code_point >= 0xc0 && code_point <= 0xd6) | ||||
|         || (code_point >= 0xd8 && code_point <= 0xf6) | ||||
|         || (code_point >= 0xf8 && code_point <= 0x2ff) | ||||
|         || (code_point >= 0x370 && code_point <= 0x37d) | ||||
|         || (code_point >= 0x37f && code_point <= 0x1fff) | ||||
|         || (code_point >= 0x200c && code_point <= 0x200d) | ||||
|         || (code_point >= 0x2070 && code_point <= 0x218f) | ||||
|         || (code_point >= 0x2c00 && code_point <= 0x2fef) | ||||
|         || (code_point >= 0x3001 && code_point <= 0xD7ff) | ||||
|         || (code_point >= 0xf900 && code_point <= 0xfdcf) | ||||
|         || (code_point >= 0xfdf0 && code_point <= 0xfffd) | ||||
|         || (code_point >= 0x10000 && code_point <= 0xeffff); | ||||
| } | ||||
| 
 | ||||
| // https://www.w3.org/TR/xml/#NT-NameChar
 | ||||
| static inline bool is_valid_name_character(u32 code_point) | ||||
| { | ||||
|     return is_valid_name_start_character(code_point) | ||||
|         || code_point == '-' | ||||
|         || code_point == '.' | ||||
|         || (code_point >= '0' && code_point <= '9') | ||||
|         || code_point == 0xb7 | ||||
|         || (code_point >= 0x300 && code_point <= 0x36f) | ||||
|         || (code_point >= 0x203f && code_point <= 0x2040); | ||||
| } | ||||
| 
 | ||||
| bool Document::is_valid_name(String const& name) | ||||
| { | ||||
|     if (name.is_empty()) | ||||
|         return false; | ||||
| 
 | ||||
|     if (!is_valid_name_start_character(name[0])) | ||||
|         return false; | ||||
| 
 | ||||
|     for (size_t i = 1; i < name.length(); ++i) { | ||||
|         if (!is_valid_name_character(name[i])) | ||||
|             return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -177,8 +177,8 @@ public: | |||
| 
 | ||||
|     JS::Value run_javascript(StringView source, StringView filename = "(unknown)"); | ||||
| 
 | ||||
|     NonnullRefPtr<Element> create_element(const String& tag_name); | ||||
|     NonnullRefPtr<Element> create_element_ns(const String& namespace_, const String& qualified_name); | ||||
|     ExceptionOr<NonnullRefPtr<Element>> create_element(const String& tag_name); | ||||
|     ExceptionOr<NonnullRefPtr<Element>> create_element_ns(const String& namespace_, const String& qualified_name); | ||||
|     NonnullRefPtr<DocumentFragment> create_document_fragment(); | ||||
|     NonnullRefPtr<Text> create_text_node(const String& data); | ||||
|     NonnullRefPtr<Comment> create_comment(const String& data); | ||||
|  | @ -316,6 +316,8 @@ public: | |||
|     void set_parser(Badge<HTML::HTMLParser>, HTML::HTMLParser&); | ||||
|     void detach_parser(Badge<HTML::HTMLParser>); | ||||
| 
 | ||||
|     static bool is_valid_name(String const&); | ||||
| 
 | ||||
| private: | ||||
|     explicit Document(const AK::URL&); | ||||
| 
 | ||||
|  |  | |||
|  | @ -171,7 +171,7 @@ void HTMLInputElement::create_shadow_tree_if_needed() | |||
|     auto initial_value = attribute(HTML::AttributeNames::value); | ||||
|     if (initial_value.is_null()) | ||||
|         initial_value = String::empty(); | ||||
|     auto element = document().create_element(HTML::TagNames::div); | ||||
|     auto element = document().create_element(HTML::TagNames::div).release_value(); | ||||
|     element->set_attribute(HTML::AttributeNames::style, "white-space: pre; padding-top: 1px; padding-bottom: 1px; padding-left: 2px; padding-right: 2px"); | ||||
|     m_text_node = adopt_ref(*new DOM::Text(document(), initial_value)); | ||||
|     m_text_node->set_always_editable(true); | ||||
|  |  | |||
|  | @ -53,21 +53,21 @@ static bool build_markdown_document(DOM::Document& document, const ByteBuffer& d | |||
| 
 | ||||
| static bool build_text_document(DOM::Document& document, const ByteBuffer& data) | ||||
| { | ||||
|     auto html_element = document.create_element("html"); | ||||
|     auto html_element = document.create_element("html").release_value(); | ||||
|     document.append_child(html_element); | ||||
| 
 | ||||
|     auto head_element = document.create_element("head"); | ||||
|     auto head_element = document.create_element("head").release_value(); | ||||
|     html_element->append_child(head_element); | ||||
|     auto title_element = document.create_element("title"); | ||||
|     auto title_element = document.create_element("title").release_value(); | ||||
|     head_element->append_child(title_element); | ||||
| 
 | ||||
|     auto title_text = document.create_text_node(document.url().basename()); | ||||
|     title_element->append_child(title_text); | ||||
| 
 | ||||
|     auto body_element = document.create_element("body"); | ||||
|     auto body_element = document.create_element("body").release_value(); | ||||
|     html_element->append_child(body_element); | ||||
| 
 | ||||
|     auto pre_element = document.create_element("pre"); | ||||
|     auto pre_element = document.create_element("pre").release_value(); | ||||
|     body_element->append_child(pre_element); | ||||
| 
 | ||||
|     pre_element->append_child(document.create_text_node(String::copy(data))); | ||||
|  | @ -85,22 +85,22 @@ static bool build_image_document(DOM::Document& document, ByteBuffer const& data | |||
|     if (!bitmap) | ||||
|         return false; | ||||
| 
 | ||||
|     auto html_element = document.create_element("html"); | ||||
|     auto html_element = document.create_element("html").release_value(); | ||||
|     document.append_child(html_element); | ||||
| 
 | ||||
|     auto head_element = document.create_element("head"); | ||||
|     auto head_element = document.create_element("head").release_value(); | ||||
|     html_element->append_child(head_element); | ||||
|     auto title_element = document.create_element("title"); | ||||
|     auto title_element = document.create_element("title").release_value(); | ||||
|     head_element->append_child(title_element); | ||||
| 
 | ||||
|     auto basename = LexicalPath::basename(document.url().path()); | ||||
|     auto title_text = adopt_ref(*new DOM::Text(document, String::formatted("{} [{}x{}]", basename, bitmap->width(), bitmap->height()))); | ||||
|     title_element->append_child(title_text); | ||||
| 
 | ||||
|     auto body_element = document.create_element("body"); | ||||
|     auto body_element = document.create_element("body").release_value(); | ||||
|     html_element->append_child(body_element); | ||||
| 
 | ||||
|     auto image_element = document.create_element("img"); | ||||
|     auto image_element = document.create_element("img").release_value(); | ||||
|     image_element->set_attribute(HTML::AttributeNames::src, document.url().to_string()); | ||||
|     body_element->append_child(image_element); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling