diff --git a/Userland/DevTools/HackStudio/Debugger/EvaluateExpressionDialog.cpp b/Userland/DevTools/HackStudio/Debugger/EvaluateExpressionDialog.cpp index bc06aa8996f..d31f8ae12e7 100644 --- a/Userland/DevTools/HackStudio/Debugger/EvaluateExpressionDialog.cpp +++ b/Userland/DevTools/HackStudio/Debugger/EvaluateExpressionDialog.cpp @@ -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); diff --git a/Userland/Libraries/LibWeb/DOM/DOMImplementation.cpp b/Userland/Libraries/LibWeb/DOM/DOMImplementation.cpp index d064b761e6a..d233b1fd208 100644 --- a/Userland/Libraries/LibWeb/DOM/DOMImplementation.cpp +++ b/Userland/Libraries/LibWeb/DOM/DOMImplementation.cpp @@ -20,7 +20,7 @@ DOMImplementation::DOMImplementation(Document& document) } // https://dom.spec.whatwg.org/#dom-domimplementation-createdocument -NonnullRefPtr DOMImplementation::create_document(const String& namespace_, const String& qualified_name) const +ExceptionOr> 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 DOMImplementation::create_document(const String& namespa RefPtr 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. diff --git a/Userland/Libraries/LibWeb/DOM/DOMImplementation.h b/Userland/Libraries/LibWeb/DOM/DOMImplementation.h index e35ae20eadb..391ee2820eb 100644 --- a/Userland/Libraries/LibWeb/DOM/DOMImplementation.h +++ b/Userland/Libraries/LibWeb/DOM/DOMImplementation.h @@ -28,7 +28,7 @@ public: } // FIXME: Add optional DocumentType once supported by IDL - NonnullRefPtr create_document(const String&, const String&) const; + ExceptionOr> create_document(const String&, const String&) const; NonnullRefPtr create_html_document(const String& title) const; NonnullRefPtr create_document_type(String const& qualified_name, String const& public_id, String const& system_id); diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index 911049badb8..c36ce5f5d2e 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -444,7 +444,7 @@ void Document::set_title(const String& title) RefPtr title_element = head_element->first_child_of_type(); if (!title_element) { - title_element = static_ptr_cast(create_element(HTML::TagNames::title)); + title_element = static_ptr_cast(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 Document::create_element(const String& tag_name) +DOM::ExceptionOr> 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 Document::create_element_ns(const String& namespace_, const String& qualified_name) +DOM::ExceptionOr> 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) 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; +} + } diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h index f24e540fa73..19c382bd143 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.h +++ b/Userland/Libraries/LibWeb/DOM/Document.h @@ -177,8 +177,8 @@ public: JS::Value run_javascript(StringView source, StringView filename = "(unknown)"); - NonnullRefPtr create_element(const String& tag_name); - NonnullRefPtr create_element_ns(const String& namespace_, const String& qualified_name); + ExceptionOr> create_element(const String& tag_name); + ExceptionOr> create_element_ns(const String& namespace_, const String& qualified_name); NonnullRefPtr create_document_fragment(); NonnullRefPtr create_text_node(const String& data); NonnullRefPtr create_comment(const String& data); @@ -316,6 +316,8 @@ public: void set_parser(Badge, HTML::HTMLParser&); void detach_parser(Badge); + static bool is_valid_name(String const&); + private: explicit Document(const AK::URL&); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 03bce5f0b4b..c62788ae25e 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -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); diff --git a/Userland/Libraries/LibWeb/Loader/FrameLoader.cpp b/Userland/Libraries/LibWeb/Loader/FrameLoader.cpp index ded5417615d..346b18c62c1 100644 --- a/Userland/Libraries/LibWeb/Loader/FrameLoader.cpp +++ b/Userland/Libraries/LibWeb/Loader/FrameLoader.cpp @@ -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);