summaryrefslogtreecommitdiff
path: root/Libraries
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-06-21 11:39:32 +0200
committerAndreas Kling <kling@serenityos.org>2020-06-21 11:39:32 +0200
commita64033e581bd82816051456cb1e3b6b21762c543 (patch)
treeb6e4721d2f0e2bf57e2e0754c9d38bf28fa54051 /Libraries
parent5eb39a5f61a2617e795b26ba95bc73d83d43a195 (diff)
downloadserenity-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.cpp157
-rw-r--r--Libraries/LibWeb/Bindings/ElementWrapper.h57
-rw-r--r--Libraries/LibWeb/Bindings/Wrapper.h1
-rw-r--r--Libraries/LibWeb/CMakeLists.txt61
-rw-r--r--Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp102
-rw-r--r--Libraries/LibWeb/DOM/Element.h4
-rw-r--r--Libraries/LibWeb/DOM/Element.idl10
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;
+
+}
+