From dcb21b0c3a3de1b0031f9ef3578823c6e0bd634f Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 13 Nov 2020 06:08:06 +0000 Subject: [PATCH] LibWeb: Add initial implementation of document.implementation --- Libraries/LibWeb/CMakeLists.txt | 2 + .../CodeGenerators/WrapperGenerator.cpp | 36 +++++++-- Libraries/LibWeb/DOM/DOMImplementation.cpp | 74 +++++++++++++++++++ Libraries/LibWeb/DOM/DOMImplementation.h | 60 +++++++++++++++ Libraries/LibWeb/DOM/DOMImplementation.idl | 7 ++ Libraries/LibWeb/DOM/Document.cpp | 8 ++ Libraries/LibWeb/DOM/Document.h | 6 ++ Libraries/LibWeb/DOM/Document.idl | 2 + Libraries/LibWeb/Forward.h | 2 + 9 files changed, 191 insertions(+), 6 deletions(-) create mode 100644 Libraries/LibWeb/DOM/DOMImplementation.cpp create mode 100644 Libraries/LibWeb/DOM/DOMImplementation.h create mode 100644 Libraries/LibWeb/DOM/DOMImplementation.idl diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 5bef2b934f..b97707bdfb 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -32,6 +32,7 @@ set(SOURCES DOM/Document.cpp DOM/DocumentFragment.cpp DOM/DocumentType.cpp + DOM/DOMImplementation.cpp DOM/Element.cpp DOM/ElementFactory.cpp DOM/EventDispatcher.cpp @@ -228,6 +229,7 @@ libweb_js_wrapper(DOM/Comment) libweb_js_wrapper(DOM/Document) libweb_js_wrapper(DOM/DocumentFragment) libweb_js_wrapper(DOM/DocumentType) +libweb_js_wrapper(DOM/DOMImplementation) libweb_js_wrapper(DOM/Element) libweb_js_wrapper(DOM/Event) libweb_js_wrapper(DOM/EventTarget) diff --git a/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp b/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp index 2d3257160f..440bc2a294 100644 --- a/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp +++ b/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp @@ -78,6 +78,7 @@ struct Type { struct Parameter { Type type; String name; + bool optional { false }; }; struct Function { @@ -88,8 +89,14 @@ struct Function { size_t length() const { - // FIXME: Take optional arguments into account - return parameters.size(); + // FIXME: This seems to produce a length that is way over what it's supposed to be. + // For example, getElementsByTagName has its length set to 20 when it should be 1. + size_t length = 0; + for (auto& parameter : parameters) { + if (!parameter.optional) + length++; + } + return length; } }; @@ -198,10 +205,13 @@ static OwnPtr parse_interface(const StringView& input) for (;;) { if (lexer.consume_specific(')')) break; + bool optional = lexer.consume_specific("optional"); + if (optional) + consume_whitespace(); auto type = parse_type(); consume_whitespace(); auto name = lexer.consume_until([](auto ch) { return isspace(ch) || ch == ',' || ch == ')'; }); - parameters.append({ move(type), move(name) }); + parameters.append({ move(type), move(name), optional }); if (lexer.consume_specific(')')) break; assert_specific(','); @@ -496,6 +506,7 @@ void generate_implementation(const IDL::Interface& interface) #include #include #include +#include #include #include #include @@ -595,7 +606,7 @@ static @fully_qualified_name@* impl_from(JS::VM& vm, JS::GlobalObject& global_ob )~~~"); } - auto generate_to_cpp = [&](auto& parameter, auto& js_name, const auto& js_suffix, auto cpp_name, bool return_void = false, bool legacy_null_to_empty_string = false) { + auto generate_to_cpp = [&](auto& parameter, auto& js_name, const auto& js_suffix, auto cpp_name, bool return_void = false, bool legacy_null_to_empty_string = false, bool optional = false) { auto scoped_generator = generator.fork(); scoped_generator.set("cpp_name", cpp_name); scoped_generator.set("js_name", js_name); @@ -608,12 +619,24 @@ static @fully_qualified_name@* impl_from(JS::VM& vm, JS::GlobalObject& global_ob else scoped_generator.set("return_statement", "return {};"); + // FIXME: Add support for optional to all types if (parameter.type.name == "DOMString") { - scoped_generator.append(R"~~~( + if (!optional) { + scoped_generator.append(R"~~~( auto @cpp_name@ = @js_name@@js_suffix@.to_string(global_object, @legacy_null_to_empty_string@); if (vm.exception()) @return_statement@ )~~~"); + } else { + scoped_generator.append(R"~~~( + String @cpp_name@; + if (!@js_name@@js_suffix@.is_undefined()) { + @cpp_name@ = @js_name@@js_suffix@.to_string(global_object, @legacy_null_to_empty_string@); + if (vm.exception()) + @return_statement@ + } +)~~~"); + } } else if (parameter.type.name == "EventListener") { scoped_generator.append(R"~~~( if (!@js_name@@js_suffix@.is_function()) { @@ -663,7 +686,8 @@ static @fully_qualified_name@* impl_from(JS::VM& vm, JS::GlobalObject& global_ob arguments_generator.append(R"~~~( auto arg@argument.index@ = vm.argument(@argument.index@); )~~~"); - generate_to_cpp(parameter, "arg", String::number(argument_index), snake_name(parameter.name), return_void); + // FIXME: Parameters can have [LegacyNullToEmptyString] attached. + generate_to_cpp(parameter, "arg", String::number(argument_index), snake_name(parameter.name), return_void, false, parameter.optional); ++argument_index; } diff --git a/Libraries/LibWeb/DOM/DOMImplementation.cpp b/Libraries/LibWeb/DOM/DOMImplementation.cpp new file mode 100644 index 0000000000..98ca186779 --- /dev/null +++ b/Libraries/LibWeb/DOM/DOMImplementation.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace Web::DOM { + +DOMImplementation::DOMImplementation(Document& document) + : m_document(document) +{ +} + +const NonnullRefPtr DOMImplementation::create_htmldocument(const String& title) const +{ + auto html_document = Document::create(); + + html_document->set_content_type("text/html"); + + auto doctype = adopt(*new DocumentType(html_document)); + doctype->set_name("html"); + html_document->append_child(doctype); + + auto html_element = create_element(html_document, HTML::TagNames::html, Namespace::HTML); + html_document->append_child(html_element); + + auto head_element = create_element(html_document, HTML::TagNames::head, Namespace::HTML); + html_element->append_child(head_element); + + if (!title.is_null()) { + auto title_element = create_element(html_document, HTML::TagNames::title, Namespace::HTML); + head_element->append_child(title_element); + + auto text_node = adopt(*new Text(html_document, title)); + title_element->append_child(text_node); + } + + auto body_element = create_element(html_document, HTML::TagNames::body, Namespace::HTML); + html_element->append_child(body_element); + + html_document->set_origin(m_document.origin()); + + return html_document; +} + +} diff --git a/Libraries/LibWeb/DOM/DOMImplementation.h b/Libraries/LibWeb/DOM/DOMImplementation.h new file mode 100644 index 0000000000..ac59fab4d0 --- /dev/null +++ b/Libraries/LibWeb/DOM/DOMImplementation.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include +#include + +namespace Web::DOM { + +class DOMImplementation final + : public RefCounted + , public Weakable + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::DOMImplementationWrapper; + + static NonnullRefPtr create(Document& document) + { + return adopt(*new DOMImplementation(document)); + } + + // FIXME: snake_case in WrapperGenerator turns "createHTMLDocument" into "create_htmldocument" + const NonnullRefPtr create_htmldocument(const String& title) const; + + // https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature + bool has_feature() const { return true; } + +private: + explicit DOMImplementation(Document&); + + Document& m_document; +}; + +} diff --git a/Libraries/LibWeb/DOM/DOMImplementation.idl b/Libraries/LibWeb/DOM/DOMImplementation.idl new file mode 100644 index 0000000000..e8e9cc6c62 --- /dev/null +++ b/Libraries/LibWeb/DOM/DOMImplementation.idl @@ -0,0 +1,7 @@ +interface DOMImplementation { + + Document createHTMLDocument(optional DOMString title); + + boolean hasFeature(); + +} diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 43f3486362..d93ed285b0 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -68,6 +68,7 @@ Document::Document(const URL& url) , m_style_sheets(CSS::StyleSheetList::create(*this)) , m_url(url) , m_window(Window::create_with_document(*this)) + , m_implementation(DOMImplementation::create(*this)) { m_style_update_timer = Core::Timer::create_single_shot(0, [this] { update_style(); @@ -136,6 +137,13 @@ Origin Document::origin() const return { m_url.protocol(), m_url.host(), m_url.port() }; } +void Document::set_origin(const Origin& origin) +{ + m_url.set_protocol(origin.protocol()); + m_url.set_host(origin.host()); + m_url.set_port(origin.port()); +} + void Document::schedule_style_update() { if (m_style_update_timer->is_active()) diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index 5aef634466..2f40dd4e20 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -64,6 +65,7 @@ public: URL url() const { return m_url; } Origin origin() const; + void set_origin(const Origin& origin); bool is_scripting_enabled() const { return true; } @@ -197,6 +199,8 @@ public: const String& content_type() const { return m_content_type; } void set_content_type(const String& content_type) { m_content_type = content_type; } + const NonnullRefPtr implementation() { return m_implementation; } + private: explicit Document(const URL&); @@ -258,6 +262,8 @@ private: String m_ready_state { "loading" }; String m_content_type; + + NonnullRefPtr m_implementation; }; } diff --git a/Libraries/LibWeb/DOM/Document.idl b/Libraries/LibWeb/DOM/Document.idl index 6aef107d91..c10a095068 100644 --- a/Libraries/LibWeb/DOM/Document.idl +++ b/Libraries/LibWeb/DOM/Document.idl @@ -1,5 +1,7 @@ interface Document : Node { + readonly attribute DOMImplementation implementation; + readonly attribute DOMString contentType; Element? getElementById(DOMString id); diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index d791837546..4ad2f6ba16 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -40,6 +40,7 @@ class Comment; class Document; class DocumentFragment; class DocumentType; +class DOMImplementation; class Element; class Event; class EventHandler; @@ -179,6 +180,7 @@ class CommentWrapper; class DocumentFragmentWrapper; class DocumentTypeWrapper; class DocumentWrapper; +class DOMImplementationWrapper; class ElementWrapper; class EventListenerWrapper; class EventTargetWrapper;