diff options
author | Andreas Kling <kling@serenityos.org> | 2020-06-21 11:39:32 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-06-21 11:39:32 +0200 |
commit | a64033e581bd82816051456cb1e3b6b21762c543 (patch) | |
tree | b6e4721d2f0e2bf57e2e0754c9d38bf28fa54051 /Libraries | |
parent | 5eb39a5f61a2617e795b26ba95bc73d83d43a195 (diff) | |
download | serenity-a64033e581bd82816051456cb1e3b6b21762c543.zip |
LibWeb: Generate Element bindings from IDL :^)
Had to do a bunch more hacking on WrapperGenerator to support this.
We now support attribute setters as well.
Diffstat (limited to 'Libraries')
-rw-r--r-- | Libraries/LibWeb/Bindings/ElementWrapper.cpp | 157 | ||||
-rw-r--r-- | Libraries/LibWeb/Bindings/ElementWrapper.h | 57 | ||||
-rw-r--r-- | Libraries/LibWeb/Bindings/Wrapper.h | 1 | ||||
-rw-r--r-- | Libraries/LibWeb/CMakeLists.txt | 61 | ||||
-rw-r--r-- | Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp | 102 | ||||
-rw-r--r-- | Libraries/LibWeb/DOM/Element.h | 4 | ||||
-rw-r--r-- | Libraries/LibWeb/DOM/Element.idl | 10 |
7 files changed, 103 insertions, 289 deletions
diff --git a/Libraries/LibWeb/Bindings/ElementWrapper.cpp b/Libraries/LibWeb/Bindings/ElementWrapper.cpp deleted file mode 100644 index 1fca778af3..0000000000 --- a/Libraries/LibWeb/Bindings/ElementWrapper.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * 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 <AK/FlyString.h> -#include <AK/Function.h> -#include <LibJS/Interpreter.h> -#include <LibJS/Runtime/Error.h> -#include <LibJS/Runtime/GlobalObject.h> -#include <LibJS/Runtime/PrimitiveString.h> -#include <LibJS/Runtime/Value.h> -#include <LibWeb/Bindings/ElementWrapper.h> -#include <LibWeb/DOM/AttributeNames.h> -#include <LibWeb/DOM/Element.h> - -namespace Web { -namespace Bindings { - -ElementWrapper::ElementWrapper(JS::GlobalObject& global_object, Element& element) - : NodeWrapper(global_object, element) -{ -} - -void ElementWrapper::initialize(JS::Interpreter& interpreter, JS::GlobalObject& global_object) -{ - NodeWrapper::initialize(interpreter, global_object); - - define_native_property("innerHTML", inner_html_getter, inner_html_setter); - define_native_property("id", id_getter, id_setter); - - u8 attributes = JS::Attribute::Configurable | JS::Attribute::Enumerable | JS::Attribute::Writable; - define_native_function("getAttribute", get_attribute, 1, attributes); - define_native_function("setAttribute", set_attribute, 2, attributes); -} - -ElementWrapper::~ElementWrapper() -{ -} - -Element& ElementWrapper::node() -{ - return static_cast<Element&>(NodeWrapper::impl()); -} - -const Element& ElementWrapper::node() const -{ - return static_cast<const Element&>(NodeWrapper::impl()); -} - -static Element* impl_from(JS::Interpreter& interpreter, JS::GlobalObject& global_object) -{ - auto* this_object = interpreter.this_value(global_object).to_object(interpreter, global_object); - if (!this_object) - return nullptr; - // FIXME: Verify that it's an ElementWrapper somehow! - return &static_cast<ElementWrapper*>(this_object)->node(); -} - -JS_DEFINE_NATIVE_FUNCTION(ElementWrapper::get_attribute) -{ - auto* impl = impl_from(interpreter, global_object); - if (!impl) - return {}; - - if (interpreter.argument_count() < 1) - return interpreter.throw_exception<JS::TypeError>(JS::ErrorType::BadArgCountOne, "getAttribute"); - - auto attribute_name = interpreter.argument(0).to_string(interpreter); - if (interpreter.exception()) - return {}; - - auto attribute_value = impl->attribute(attribute_name); - if (attribute_value.is_null()) - return JS::js_null(); - - return JS::js_string(interpreter, attribute_value); -} - -JS_DEFINE_NATIVE_FUNCTION(ElementWrapper::set_attribute) -{ - auto* impl = impl_from(interpreter, global_object); - if (!impl) - return {}; - - if (interpreter.argument_count() < 2) - return interpreter.throw_exception<JS::TypeError>(JS::ErrorType::BadArgCountMany, "setAttribute", "two"); - - auto attribute_name = interpreter.argument(0).to_string(interpreter); - if (interpreter.exception()) - return {}; - - auto attribute_value = interpreter.argument(1).to_string(interpreter); - if (interpreter.exception()) - return {}; - - impl->set_attribute(attribute_name, attribute_value); - return JS::js_undefined(); -} - -JS_DEFINE_NATIVE_GETTER(ElementWrapper::inner_html_getter) -{ - if (auto* impl = impl_from(interpreter, global_object)) - return JS::js_string(interpreter, impl->inner_html()); - return {}; -} - -JS_DEFINE_NATIVE_SETTER(ElementWrapper::inner_html_setter) -{ - if (auto* impl = impl_from(interpreter, global_object)) { - auto string = value.to_string(interpreter); - if (interpreter.exception()) - return; - impl->set_inner_html(string); - } -} - -JS_DEFINE_NATIVE_GETTER(ElementWrapper::id_getter) -{ - if (auto* impl = impl_from(interpreter, global_object)) - return JS::js_string(interpreter, impl->attribute(HTML::AttributeNames::id)); - return {}; -} - -JS_DEFINE_NATIVE_SETTER(ElementWrapper::id_setter) -{ - if (auto* impl = impl_from(interpreter, global_object)) { - auto string = value.to_string(interpreter); - if (interpreter.exception()) - return; - impl->set_attribute(HTML::AttributeNames::id, string); - } -} - -} -} diff --git a/Libraries/LibWeb/Bindings/ElementWrapper.h b/Libraries/LibWeb/Bindings/ElementWrapper.h deleted file mode 100644 index b7e53d1247..0000000000 --- a/Libraries/LibWeb/Bindings/ElementWrapper.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * 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 <LibWeb/Bindings/NodeWrapper.h> - -namespace Web { -namespace Bindings { - -class ElementWrapper : public NodeWrapper { -public: - ElementWrapper(JS::GlobalObject&, Element&); - virtual void initialize(JS::Interpreter&, JS::GlobalObject&) override; - virtual ~ElementWrapper() override; - - Element& node(); - const Element& node() const; - -private: - virtual const char* class_name() const override { return "ElementWrapper"; } - - JS_DECLARE_NATIVE_GETTER(inner_html_getter); - JS_DECLARE_NATIVE_SETTER(inner_html_setter); - - JS_DECLARE_NATIVE_GETTER(id_getter); - JS_DECLARE_NATIVE_SETTER(id_setter); - - JS_DECLARE_NATIVE_FUNCTION(get_attribute); - JS_DECLARE_NATIVE_FUNCTION(set_attribute); -}; - -} -} diff --git a/Libraries/LibWeb/Bindings/Wrapper.h b/Libraries/LibWeb/Bindings/Wrapper.h index 24c288b01c..09d9a6b5cb 100644 --- a/Libraries/LibWeb/Bindings/Wrapper.h +++ b/Libraries/LibWeb/Bindings/Wrapper.h @@ -40,6 +40,7 @@ class Wrapper public: virtual bool is_node_wrapper() const { return false; } virtual bool is_document_wrapper() const { return false; } + virtual bool is_element_wrapper() const { return false; } protected: explicit Wrapper(Object& prototype) diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 3772bad89a..5131892522 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -3,6 +3,7 @@ set(SOURCES Bindings/DocumentWrapper.cpp Bindings/DocumentWrapper.h Bindings/ElementWrapper.cpp + Bindings/ElementWrapper.h Bindings/EventListenerWrapper.cpp Bindings/EventTargetWrapper.cpp Bindings/EventWrapper.cpp @@ -126,45 +127,29 @@ set(GENERATED_SOURCES ../../Services/ProtocolServer/ProtocolServerEndpoint.h ) -add_custom_command( - OUTPUT Bindings/NodeWrapper.h - COMMAND /bin/mkdir -p Bindings - COMMAND WrapperGenerator --header ${CMAKE_CURRENT_SOURCE_DIR}/DOM/Node.idl > Bindings/NodeWrapper.h - VERBATIM - DEPENDS WrapperGenerator - MAIN_DEPENDENCY DOM/Node.idl -) -add_custom_target(generate_NodeWrapper.h DEPENDS Bindings/NodeWrapper.h) - -add_custom_command( - OUTPUT Bindings/NodeWrapper.cpp - COMMAND /bin/mkdir -p Bindings - COMMAND WrapperGenerator --implementation ${CMAKE_CURRENT_SOURCE_DIR}/DOM/Node.idl > Bindings/NodeWrapper.cpp - VERBATIM - DEPENDS WrapperGenerator - MAIN_DEPENDENCY DOM/Node.idl -) -add_custom_target(generate_NodeWrapper.cpp DEPENDS Bindings/NodeWrapper.cpp) +function(libweb_js_wrapper class) + add_custom_command( + OUTPUT Bindings/${class}Wrapper.h + COMMAND /bin/mkdir -p Bindings + COMMAND WrapperGenerator --header ${CMAKE_CURRENT_SOURCE_DIR}/DOM/${class}.idl > Bindings/${class}Wrapper.h + VERBATIM + DEPENDS WrapperGenerator + MAIN_DEPENDENCY DOM/${class}.idl + ) + add_custom_command( + OUTPUT Bindings/${class}Wrapper.cpp + COMMAND /bin/mkdir -p Bindings + COMMAND WrapperGenerator --implementation ${CMAKE_CURRENT_SOURCE_DIR}/DOM/${class}.idl > Bindings/${class}Wrapper.cpp + VERBATIM + DEPENDS WrapperGenerator + MAIN_DEPENDENCY DOM/${class}.idl + ) + add_custom_target(generate_${class}Wrapper.h DEPENDS Bindings/${class}Wrapper.h) +endfunction() -add_custom_command( - OUTPUT Bindings/DocumentWrapper.h - COMMAND /bin/mkdir -p Bindings - COMMAND WrapperGenerator --header ${CMAKE_CURRENT_SOURCE_DIR}/DOM/Document.idl > Bindings/DocumentWrapper.h - VERBATIM - DEPENDS WrapperGenerator - MAIN_DEPENDENCY DOM/Document.idl -) -add_custom_target(generate_DocumentWrapper.h DEPENDS Bindings/DocumentWrapper.h) - -add_custom_command( - OUTPUT Bindings/DocumentWrapper.cpp - COMMAND /bin/mkdir -p Bindings - COMMAND WrapperGenerator --implementation ${CMAKE_CURRENT_SOURCE_DIR}/DOM/Document.idl > Bindings/DocumentWrapper.cpp - VERBATIM - DEPENDS WrapperGenerator - MAIN_DEPENDENCY DOM/Document.idl -) -add_custom_target(generate_DocumentWrapper.cpp DEPENDS Bindings/DocumentWrapper.cpp) +libweb_js_wrapper(Node) +libweb_js_wrapper(Document) +libweb_js_wrapper(Element) add_custom_command( OUTPUT CSS/PropertyID.h diff --git a/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp b/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp index a1dcd0b860..24c32f70bc 100644 --- a/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp +++ b/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp @@ -34,15 +34,17 @@ static String snake_name(const StringView& title_name) { StringBuilder builder; bool first = true; + bool last_was_uppercase = false; for (auto ch : title_name) { if (isupper(ch)) { - if (!first) + if (!first && !last_was_uppercase) builder.append('_'); builder.append(tolower(ch)); } else { builder.append(ch); } first = false; + last_was_uppercase = isupper(ch); } return builder.to_string(); } @@ -93,20 +95,6 @@ struct Interface { String wrapper_base_class; }; -static String snake_name(const StringView& camel_name) -{ - StringBuilder builder; - for (auto ch : camel_name) { - if (isupper(ch)) { - builder.append('_'); - builder.append(tolower(ch)); - } else { - builder.append(ch); - } - } - return builder.to_string(); -} - OwnPtr<Interface> parse_interface(const StringView& input) { auto interface = make<Interface>(); @@ -422,9 +410,55 @@ void generate_implementation(const IDL::Interface& interface) out() << " return &static_cast<" << wrapper_class << "*>(this_object)->impl();"; out() << "}"; + auto generate_to_cpp = [&](auto& parameter, auto& js_name, auto& js_suffix, auto cpp_name, bool return_void = false) { + auto generate_return = [&] { + if (return_void) + out() << " return;"; + else + out() << " return {};"; + }; + if (parameter.type.name == "DOMString") { + out() << " auto " << cpp_name << " = " << js_name << js_suffix << ".to_string(interpreter);"; + out() << " if (interpreter.exception())"; + generate_return(); + } else if (parameter.type.name == "Node") { + out() << " auto " << cpp_name << "_object = " << js_name << js_suffix << ".to_object(interpreter, global_object);"; + out() << " if (interpreter.exception())"; + generate_return(); + auto is_foo_wrapper_name = snake_name(String::format("Is%sWrapper", parameter.type.name.characters())); + out() << " if (!" << cpp_name << "_object->is_web_wrapper() || !static_cast<Wrapper*>(" << cpp_name << "_object)->" << is_foo_wrapper_name << "()) {"; + out() << " interpreter.throw_exception<JS::TypeError>(JS::ErrorType::NotA, \"" << parameter.type.name << "\");"; + generate_return(); + out() << " }"; + out() << " auto& " << cpp_name << " = static_cast<" << parameter.type.name << "Wrapper*>(" << cpp_name << "_object)->impl();"; + } + }; + + auto generate_arguments = [&](auto& parameters, auto& arguments_builder, bool return_void = false) { + Vector<String> parameter_names; + size_t argument_index = 0; + for (auto& parameter : parameters) { + parameter_names.append(snake_name(parameter.name)); + out() << " auto arg" << argument_index << " = interpreter.argument(" << argument_index << ");"; + generate_to_cpp(parameter, "arg", argument_index, snake_name(parameter.name), return_void); + ++argument_index; + } + + arguments_builder.join(", ", parameter_names); + }; + auto generate_return_statement = [&](auto& return_type) { + if (return_type.name == "void") { + out() << " return JS::js_undefined();"; + return; + } + if (return_type.nullable) { - out() << " if (!retval)"; + if (return_type.name == "DOMString") { + out() << " if (retval.is_null())"; + } else { + out() << " if (!retval)"; + } out() << " return JS::js_null();"; } @@ -445,7 +479,7 @@ void generate_implementation(const IDL::Interface& interface) // Implementation: Attributes for (auto& attribute : interface.attributes) { - out() << "JS_DEFINE_NATIVE_GETTER(" << wrapper_class << "::" << snake_name(attribute.name) << "_getter)"; + out() << "JS_DEFINE_NATIVE_GETTER(" << wrapper_class << "::" << attribute.getter_callback_name << ")"; out() << "{"; out() << " auto* impl = impl_from(interpreter, global_object);"; out() << " if (!impl)"; @@ -453,6 +487,19 @@ void generate_implementation(const IDL::Interface& interface) out() << " auto retval = impl->" << snake_name(attribute.name) << "();"; generate_return_statement(attribute.type); out() << "}"; + + if (!attribute.readonly) { + out() << "JS_DEFINE_NATIVE_SETTER(" << wrapper_class << "::" << attribute.setter_callback_name << ")"; + out() << "{"; + out() << " auto* impl = impl_from(interpreter, global_object);"; + out() << " if (!impl)"; + out() << " return;"; + + generate_to_cpp(attribute, "value", "", "cpp_value", true); + + out() << " impl->set_" << snake_name(attribute.name) << "(cpp_value);"; + out() << "}"; + } } // Implementation: Functions @@ -465,27 +512,8 @@ void generate_implementation(const IDL::Interface& interface) out() << " if (interpreter.argument_count() < " << function.length() << ")"; out() << " return interpreter.throw_exception<JS::TypeError>(JS::ErrorType::BadArgCountMany, \"" << function.name << "\", \"" << function.length() << "\");"; - Vector<String> parameter_names; - size_t argument_index = 0; - for (auto& parameter : function.parameters) { - parameter_names.append(snake_name(parameter.name)); - if (parameter.type.name == "DOMString") { - out() << " auto " << snake_name(parameter.name) << " = interpreter.argument(" << argument_index << ").to_string(interpreter);"; - out() << " if (interpreter.exception())"; - out() << " return {};"; - } else if (parameter.type.name == "Node") { - out() << " auto " << snake_name(parameter.name) << "_object = interpreter.argument(" << argument_index << ").to_object(interpreter, global_object);"; - out() << " if (interpreter.exception())"; - out() << " return {};"; - out() << " if (!" << snake_name(parameter.name) << "_object->is_web_wrapper() || !static_cast<Wrapper*>(" << snake_name(parameter.name) << "_object)->is_node_wrapper())"; - out() << " return interpreter.throw_exception<JS::TypeError>(JS::ErrorType::NotA, \"" << parameter.type.name << "\");"; - out() << " auto& " << snake_name(parameter.name) << " = static_cast<" << wrapper_class << "*>(" << snake_name(parameter.name) << "_object)->impl();"; - } - ++argument_index; - } - StringBuilder arguments_builder; - arguments_builder.join(", ", parameter_names); + generate_arguments(function.parameters, arguments_builder); if (function.return_type.name != "void") { out() << " auto retval = impl->" << snake_name(function.name) << "(" << arguments_builder.to_string() << ");"; diff --git a/Libraries/LibWeb/DOM/Element.h b/Libraries/LibWeb/DOM/Element.h index 547b6b21e3..70f93dbdc5 100644 --- a/Libraries/LibWeb/DOM/Element.h +++ b/Libraries/LibWeb/DOM/Element.h @@ -50,6 +50,7 @@ public: bool has_attribute(const FlyString& name) const { return !attribute(name).is_null(); } String attribute(const FlyString& name) const; + String get_attribute(const FlyString& name) const { return attribute(name); } void set_attribute(const FlyString& name, const String& value); void set_attributes(Vector<Attribute>&&); @@ -80,6 +81,9 @@ public: String inner_html() const; void set_inner_html(StringView); + String id() const { return attribute(HTML::AttributeNames::id); } + void set_id(const String& value) { set_attribute(HTML::AttributeNames::id, value); } + protected: RefPtr<LayoutNode> create_layout_node(const StyleProperties* parent_style) const override; diff --git a/Libraries/LibWeb/DOM/Element.idl b/Libraries/LibWeb/DOM/Element.idl new file mode 100644 index 0000000000..af914db646 --- /dev/null +++ b/Libraries/LibWeb/DOM/Element.idl @@ -0,0 +1,10 @@ +interface Element : Node { + + DOMString? getAttribute(DOMString qualifiedName); + void setAttribute(DOMString qualifiedName, DOMString value); + + attribute DOMString innerHTML; + attribute DOMString id; + +} + |