diff options
Diffstat (limited to 'Userland/Libraries/LibWeb')
538 files changed, 47674 insertions, 0 deletions
diff --git a/Userland/Libraries/LibWeb/Bindings/EventListenerWrapper.cpp b/Userland/Libraries/LibWeb/Bindings/EventListenerWrapper.cpp new file mode 100644 index 0000000000..2f9cbac2c2 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/EventListenerWrapper.cpp @@ -0,0 +1,46 @@ +/* + * 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 <LibJS/Runtime/Function.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibWeb/Bindings/EventListenerWrapper.h> +#include <LibWeb/DOM/EventListener.h> + +namespace Web { +namespace Bindings { + +EventListenerWrapper::EventListenerWrapper(JS::GlobalObject& global_object, DOM::EventListener& impl) + : Wrapper(*global_object.object_prototype()) + , m_impl(impl) +{ +} + +EventListenerWrapper::~EventListenerWrapper() +{ +} + +} +} diff --git a/Userland/Libraries/LibWeb/Bindings/EventListenerWrapper.h b/Userland/Libraries/LibWeb/Bindings/EventListenerWrapper.h new file mode 100644 index 0000000000..fdd74108ea --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/EventListenerWrapper.h @@ -0,0 +1,49 @@ +/* + * 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/Wrapper.h> + +namespace Web { +namespace Bindings { + +class EventListenerWrapper final : public Wrapper { + JS_OBJECT(EventListenerWrapper, Wrapper); + +public: + EventListenerWrapper(JS::GlobalObject&, DOM::EventListener&); + virtual ~EventListenerWrapper() override; + + DOM::EventListener& impl() { return *m_impl; } + const DOM::EventListener& impl() const { return *m_impl; } + +private: + NonnullRefPtr<DOM::EventListener> m_impl; +}; + +} +} diff --git a/Userland/Libraries/LibWeb/Bindings/EventTargetWrapperFactory.cpp b/Userland/Libraries/LibWeb/Bindings/EventTargetWrapperFactory.cpp new file mode 100644 index 0000000000..4a6e6344e9 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/EventTargetWrapperFactory.cpp @@ -0,0 +1,37 @@ +/* + * 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 <LibWeb/Bindings/EventTargetWrapperFactory.h> +#include <LibWeb/DOM/EventTarget.h> + +namespace Web::Bindings { + +EventTargetWrapper* wrap(JS::GlobalObject& global_object, DOM::EventTarget& target) +{ + return target.create_wrapper(global_object); +} + +} diff --git a/Userland/Libraries/LibWeb/Bindings/EventTargetWrapperFactory.h b/Userland/Libraries/LibWeb/Bindings/EventTargetWrapperFactory.h new file mode 100644 index 0000000000..c28c7724ca --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/EventTargetWrapperFactory.h @@ -0,0 +1,36 @@ +/* + * 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 <LibJS/Forward.h> +#include <LibWeb/Forward.h> + +namespace Web::Bindings { + +EventTargetWrapper* wrap(JS::GlobalObject&, DOM::EventTarget&); + +} diff --git a/Userland/Libraries/LibWeb/Bindings/EventWrapperFactory.cpp b/Userland/Libraries/LibWeb/Bindings/EventWrapperFactory.cpp new file mode 100644 index 0000000000..3064799cf0 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/EventWrapperFactory.cpp @@ -0,0 +1,42 @@ +/* + * 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 <LibWeb/Bindings/EventWrapper.h> +#include <LibWeb/Bindings/EventWrapperFactory.h> +#include <LibWeb/Bindings/MouseEventWrapper.h> + +namespace Web { +namespace Bindings { + +EventWrapper* wrap(JS::GlobalObject& global_object, DOM::Event& event) +{ + if (is<UIEvents::MouseEvent>(event)) + return static_cast<MouseEventWrapper*>(wrap_impl(global_object, static_cast<UIEvents::MouseEvent&>(event))); + return static_cast<EventWrapper*>(wrap_impl(global_object, event)); +} + +} +} diff --git a/Userland/Libraries/LibWeb/Bindings/EventWrapperFactory.h b/Userland/Libraries/LibWeb/Bindings/EventWrapperFactory.h new file mode 100644 index 0000000000..ebe82c5e88 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/EventWrapperFactory.h @@ -0,0 +1,36 @@ +/* + * 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 <LibJS/Forward.h> +#include <LibWeb/Forward.h> + +namespace Web::Bindings { + +EventWrapper* wrap(JS::GlobalObject&, DOM::Event&); + +} diff --git a/Userland/Libraries/LibWeb/Bindings/LocationObject.cpp b/Userland/Libraries/LibWeb/Bindings/LocationObject.cpp new file mode 100644 index 0000000000..93d00d8af7 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/LocationObject.cpp @@ -0,0 +1,146 @@ +/* + * 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/StringBuilder.h> +#include <LibWeb/Bindings/LocationObject.h> +#include <LibWeb/Bindings/WindowObject.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Window.h> + +namespace Web { +namespace Bindings { + +LocationObject::LocationObject(JS::GlobalObject& global_object) + : Object(*global_object.object_prototype()) +{ +} + +void LocationObject::initialize(JS::GlobalObject& global_object) +{ + Object::initialize(global_object); + u8 attr = JS::Attribute::Writable | JS::Attribute::Enumerable; + define_native_property("href", href_getter, href_setter, attr); + define_native_property("host", host_getter, nullptr, attr); + define_native_property("hostname", hostname_getter, nullptr, attr); + define_native_property("pathname", pathname_getter, nullptr, attr); + define_native_property("hash", hash_getter, nullptr, attr); + define_native_property("search", search_getter, nullptr, attr); + define_native_property("protocol", protocol_getter, nullptr, attr); + + define_native_function("reload", reload, 0, JS::Attribute::Enumerable); +} + +LocationObject::~LocationObject() +{ +} + +JS_DEFINE_NATIVE_GETTER(LocationObject::href_getter) +{ + auto& window = static_cast<WindowObject&>(global_object); + return JS::js_string(vm, window.impl().document().url().to_string()); +} + +JS_DEFINE_NATIVE_SETTER(LocationObject::href_setter) +{ + auto& window = static_cast<WindowObject&>(global_object); + auto new_href = value.to_string(global_object); + if (vm.exception()) + return; + auto href_url = window.impl().document().complete_url(new_href); + if (!href_url.is_valid()) { + vm.throw_exception<JS::URIError>(global_object, String::formatted("Invalid URL '{}'", new_href)); + return; + } + window.impl().did_set_location_href({}, href_url); +} + +JS_DEFINE_NATIVE_GETTER(LocationObject::pathname_getter) +{ + auto& window = static_cast<WindowObject&>(global_object); + return JS::js_string(vm, window.impl().document().url().path()); +} + +JS_DEFINE_NATIVE_GETTER(LocationObject::hostname_getter) +{ + auto& window = static_cast<WindowObject&>(global_object); + return JS::js_string(vm, window.impl().document().url().host()); +} + +JS_DEFINE_NATIVE_GETTER(LocationObject::host_getter) +{ + auto& window = static_cast<WindowObject&>(global_object); + auto url = window.impl().document().url(); + StringBuilder builder; + builder.append(url.host()); + builder.append(':'); + builder.appendf("%u", url.port()); + return JS::js_string(vm, builder.to_string()); +} + +JS_DEFINE_NATIVE_GETTER(LocationObject::hash_getter) +{ + auto& window = static_cast<WindowObject&>(global_object); + auto fragment = window.impl().document().url().fragment(); + if (!fragment.length()) + return JS::js_string(vm, ""); + StringBuilder builder; + builder.append('#'); + builder.append(fragment); + return JS::js_string(vm, builder.to_string()); +} + +JS_DEFINE_NATIVE_GETTER(LocationObject::search_getter) +{ + auto& window = static_cast<WindowObject&>(global_object); + auto query = window.impl().document().url().query(); + if (!query.length()) + return JS::js_string(vm, ""); + StringBuilder builder; + builder.append('?'); + builder.append(query); + return JS::js_string(vm, builder.to_string()); +} + +JS_DEFINE_NATIVE_GETTER(LocationObject::protocol_getter) +{ + auto& window = static_cast<WindowObject&>(global_object); + StringBuilder builder; + builder.append(window.impl().document().url().protocol()); + builder.append(':'); + return JS::js_string(vm, builder.to_string()); +} + +JS_DEFINE_NATIVE_FUNCTION(LocationObject::reload) +{ + auto& window = static_cast<WindowObject&>(global_object); + window.impl().did_call_location_reload({}); + return JS::js_undefined(); +} + +} + +} diff --git a/Userland/Libraries/LibWeb/Bindings/LocationObject.h b/Userland/Libraries/LibWeb/Bindings/LocationObject.h new file mode 100644 index 0000000000..76a38946c1 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/LocationObject.h @@ -0,0 +1,58 @@ +/* + * 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 <LibJS/Runtime/Object.h> +#include <LibWeb/Forward.h> + +namespace Web { +namespace Bindings { + +class LocationObject final : public JS::Object { + JS_OBJECT(LocationObject, JS::Object); + +public: + explicit LocationObject(JS::GlobalObject&); + virtual void initialize(JS::GlobalObject&) override; + virtual ~LocationObject() override; + +private: + JS_DECLARE_NATIVE_FUNCTION(reload); + + JS_DECLARE_NATIVE_GETTER(href_getter); + JS_DECLARE_NATIVE_SETTER(href_setter); + + JS_DECLARE_NATIVE_GETTER(host_getter); + JS_DECLARE_NATIVE_GETTER(hostname_getter); + JS_DECLARE_NATIVE_GETTER(pathname_getter); + JS_DECLARE_NATIVE_GETTER(hash_getter); + JS_DECLARE_NATIVE_GETTER(search_getter); + JS_DECLARE_NATIVE_GETTER(protocol_getter); +}; + +} +} diff --git a/Userland/Libraries/LibWeb/Bindings/NavigatorObject.cpp b/Userland/Libraries/LibWeb/Bindings/NavigatorObject.cpp new file mode 100644 index 0000000000..248097d8e3 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/NavigatorObject.cpp @@ -0,0 +1,69 @@ +/* + * 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 <LibJS/Runtime/Array.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibWeb/Bindings/NavigatorObject.h> +#include <LibWeb/Loader/ResourceLoader.h> + +namespace Web { +namespace Bindings { + +NavigatorObject::NavigatorObject(JS::GlobalObject& global_object) + : Object(*global_object.object_prototype()) +{ +} + +void NavigatorObject::initialize(JS::GlobalObject& global_object) +{ + auto& heap = this->heap(); + auto* languages = JS::Array::create(global_object); + languages->indexed_properties().append(js_string(heap, "en-US")); + + define_property("appCodeName", js_string(heap, "Mozilla")); + define_property("appName", js_string(heap, "Netscape")); + define_property("appVersion", js_string(heap, "4.0")); + define_property("language", languages->get(0)); + define_property("languages", languages); + define_property("platform", js_string(heap, "SerenityOS")); + define_property("product", js_string(heap, "Gecko")); + + define_native_property("userAgent", user_agent_getter, nullptr); +} + +NavigatorObject::~NavigatorObject() +{ +} + +JS_DEFINE_NATIVE_GETTER(NavigatorObject::user_agent_getter) +{ + return JS::js_string(vm, ResourceLoader::the().user_agent()); +} + +} + +} diff --git a/Userland/Libraries/LibWeb/Bindings/NavigatorObject.h b/Userland/Libraries/LibWeb/Bindings/NavigatorObject.h new file mode 100644 index 0000000000..fe10271e96 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/NavigatorObject.h @@ -0,0 +1,48 @@ +/* + * 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 <LibJS/Runtime/Object.h> +#include <LibWeb/Forward.h> + +namespace Web { +namespace Bindings { + +class NavigatorObject final : public JS::Object { + JS_OBJECT(NavigatorObject, JS::Object); + +public: + NavigatorObject(JS::GlobalObject&); + virtual void initialize(JS::GlobalObject&) override; + virtual ~NavigatorObject() override; + +private: + JS_DECLARE_NATIVE_GETTER(user_agent_getter); +}; + +} +} diff --git a/Userland/Libraries/LibWeb/Bindings/NodeWrapperFactory.cpp b/Userland/Libraries/LibWeb/Bindings/NodeWrapperFactory.cpp new file mode 100644 index 0000000000..f78491d33a --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/NodeWrapperFactory.cpp @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2020, Luke Wilde <luke.wilde@live.co.uk> + * 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 <LibWeb/Bindings/CharacterDataWrapper.h> +#include <LibWeb/Bindings/CommentWrapper.h> +#include <LibWeb/Bindings/DocumentFragmentWrapper.h> +#include <LibWeb/Bindings/DocumentTypeWrapper.h> +#include <LibWeb/Bindings/DocumentWrapper.h> +#include <LibWeb/Bindings/HTMLAnchorElementWrapper.h> +#include <LibWeb/Bindings/HTMLAreaElementWrapper.h> +#include <LibWeb/Bindings/HTMLAudioElementWrapper.h> +#include <LibWeb/Bindings/HTMLBRElementWrapper.h> +#include <LibWeb/Bindings/HTMLBaseElementWrapper.h> +#include <LibWeb/Bindings/HTMLBodyElementWrapper.h> +#include <LibWeb/Bindings/HTMLButtonElementWrapper.h> +#include <LibWeb/Bindings/HTMLCanvasElementWrapper.h> +#include <LibWeb/Bindings/HTMLDListElementWrapper.h> +#include <LibWeb/Bindings/HTMLDataElementWrapper.h> +#include <LibWeb/Bindings/HTMLDataListElementWrapper.h> +#include <LibWeb/Bindings/HTMLDetailsElementWrapper.h> +#include <LibWeb/Bindings/HTMLDialogElementWrapper.h> +#include <LibWeb/Bindings/HTMLDirectoryElementWrapper.h> +#include <LibWeb/Bindings/HTMLDivElementWrapper.h> +#include <LibWeb/Bindings/HTMLElementWrapper.h> +#include <LibWeb/Bindings/HTMLEmbedElementWrapper.h> +#include <LibWeb/Bindings/HTMLFieldSetElementWrapper.h> +#include <LibWeb/Bindings/HTMLFontElementWrapper.h> +#include <LibWeb/Bindings/HTMLFormElementWrapper.h> +#include <LibWeb/Bindings/HTMLFrameElementWrapper.h> +#include <LibWeb/Bindings/HTMLFrameSetElementWrapper.h> +#include <LibWeb/Bindings/HTMLHRElementWrapper.h> +#include <LibWeb/Bindings/HTMLHeadElementWrapper.h> +#include <LibWeb/Bindings/HTMLHeadingElementWrapper.h> +#include <LibWeb/Bindings/HTMLHtmlElementWrapper.h> +#include <LibWeb/Bindings/HTMLIFrameElementWrapper.h> +#include <LibWeb/Bindings/HTMLImageElementWrapper.h> +#include <LibWeb/Bindings/HTMLInputElementWrapper.h> +#include <LibWeb/Bindings/HTMLLIElementWrapper.h> +#include <LibWeb/Bindings/HTMLLabelElementWrapper.h> +#include <LibWeb/Bindings/HTMLLegendElementWrapper.h> +#include <LibWeb/Bindings/HTMLLinkElementWrapper.h> +#include <LibWeb/Bindings/HTMLMapElementWrapper.h> +#include <LibWeb/Bindings/HTMLMarqueeElementWrapper.h> +#include <LibWeb/Bindings/HTMLMenuElementWrapper.h> +#include <LibWeb/Bindings/HTMLMetaElementWrapper.h> +#include <LibWeb/Bindings/HTMLMeterElementWrapper.h> +#include <LibWeb/Bindings/HTMLModElementWrapper.h> +#include <LibWeb/Bindings/HTMLOListElementWrapper.h> +#include <LibWeb/Bindings/HTMLObjectElementWrapper.h> +#include <LibWeb/Bindings/HTMLOptGroupElementWrapper.h> +#include <LibWeb/Bindings/HTMLOptionElementWrapper.h> +#include <LibWeb/Bindings/HTMLOutputElementWrapper.h> +#include <LibWeb/Bindings/HTMLParagraphElementWrapper.h> +#include <LibWeb/Bindings/HTMLParamElementWrapper.h> +#include <LibWeb/Bindings/HTMLPictureElementWrapper.h> +#include <LibWeb/Bindings/HTMLPreElementWrapper.h> +#include <LibWeb/Bindings/HTMLProgressElementWrapper.h> +#include <LibWeb/Bindings/HTMLQuoteElementWrapper.h> +#include <LibWeb/Bindings/HTMLScriptElementWrapper.h> +#include <LibWeb/Bindings/HTMLSelectElementWrapper.h> +#include <LibWeb/Bindings/HTMLSlotElementWrapper.h> +#include <LibWeb/Bindings/HTMLSourceElementWrapper.h> +#include <LibWeb/Bindings/HTMLSpanElementWrapper.h> +#include <LibWeb/Bindings/HTMLStyleElementWrapper.h> +#include <LibWeb/Bindings/HTMLTableCaptionElementWrapper.h> +#include <LibWeb/Bindings/HTMLTableCellElementWrapper.h> +#include <LibWeb/Bindings/HTMLTableColElementWrapper.h> +#include <LibWeb/Bindings/HTMLTableElementWrapper.h> +#include <LibWeb/Bindings/HTMLTableRowElementWrapper.h> +#include <LibWeb/Bindings/HTMLTableSectionElementWrapper.h> +#include <LibWeb/Bindings/HTMLTemplateElementWrapper.h> +#include <LibWeb/Bindings/HTMLTextAreaElementWrapper.h> +#include <LibWeb/Bindings/HTMLTimeElementWrapper.h> +#include <LibWeb/Bindings/HTMLTitleElementWrapper.h> +#include <LibWeb/Bindings/HTMLTrackElementWrapper.h> +#include <LibWeb/Bindings/HTMLUListElementWrapper.h> +#include <LibWeb/Bindings/HTMLUnknownElementWrapper.h> +#include <LibWeb/Bindings/HTMLVideoElementWrapper.h> +#include <LibWeb/Bindings/NodeWrapper.h> +#include <LibWeb/Bindings/NodeWrapperFactory.h> +#include <LibWeb/Bindings/SVGPathElementWrapper.h> +#include <LibWeb/Bindings/SVGSVGElementWrapper.h> +#include <LibWeb/Bindings/TextWrapper.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Node.h> +#include <LibWeb/HTML/HTMLAnchorElement.h> +#include <LibWeb/HTML/HTMLAreaElement.h> +#include <LibWeb/HTML/HTMLAudioElement.h> +#include <LibWeb/HTML/HTMLBRElement.h> +#include <LibWeb/HTML/HTMLBaseElement.h> +#include <LibWeb/HTML/HTMLBodyElement.h> +#include <LibWeb/HTML/HTMLButtonElement.h> +#include <LibWeb/HTML/HTMLCanvasElement.h> +#include <LibWeb/HTML/HTMLDListElement.h> +#include <LibWeb/HTML/HTMLDataElement.h> +#include <LibWeb/HTML/HTMLDataListElement.h> +#include <LibWeb/HTML/HTMLDetailsElement.h> +#include <LibWeb/HTML/HTMLDialogElement.h> +#include <LibWeb/HTML/HTMLDirectoryElement.h> +#include <LibWeb/HTML/HTMLDivElement.h> +#include <LibWeb/HTML/HTMLEmbedElement.h> +#include <LibWeb/HTML/HTMLFieldSetElement.h> +#include <LibWeb/HTML/HTMLFontElement.h> +#include <LibWeb/HTML/HTMLFormElement.h> +#include <LibWeb/HTML/HTMLFrameElement.h> +#include <LibWeb/HTML/HTMLFrameSetElement.h> +#include <LibWeb/HTML/HTMLHRElement.h> +#include <LibWeb/HTML/HTMLHeadElement.h> +#include <LibWeb/HTML/HTMLHeadingElement.h> +#include <LibWeb/HTML/HTMLHtmlElement.h> +#include <LibWeb/HTML/HTMLIFrameElement.h> +#include <LibWeb/HTML/HTMLImageElement.h> +#include <LibWeb/HTML/HTMLInputElement.h> +#include <LibWeb/HTML/HTMLLIElement.h> +#include <LibWeb/HTML/HTMLLabelElement.h> +#include <LibWeb/HTML/HTMLLegendElement.h> +#include <LibWeb/HTML/HTMLLinkElement.h> +#include <LibWeb/HTML/HTMLMapElement.h> +#include <LibWeb/HTML/HTMLMarqueeElement.h> +#include <LibWeb/HTML/HTMLMenuElement.h> +#include <LibWeb/HTML/HTMLMetaElement.h> +#include <LibWeb/HTML/HTMLMeterElement.h> +#include <LibWeb/HTML/HTMLModElement.h> +#include <LibWeb/HTML/HTMLOListElement.h> +#include <LibWeb/HTML/HTMLObjectElement.h> +#include <LibWeb/HTML/HTMLOptGroupElement.h> +#include <LibWeb/HTML/HTMLOptionElement.h> +#include <LibWeb/HTML/HTMLOutputElement.h> +#include <LibWeb/HTML/HTMLParagraphElement.h> +#include <LibWeb/HTML/HTMLParamElement.h> +#include <LibWeb/HTML/HTMLPictureElement.h> +#include <LibWeb/HTML/HTMLPreElement.h> +#include <LibWeb/HTML/HTMLProgressElement.h> +#include <LibWeb/HTML/HTMLQuoteElement.h> +#include <LibWeb/HTML/HTMLScriptElement.h> +#include <LibWeb/HTML/HTMLSelectElement.h> +#include <LibWeb/HTML/HTMLSlotElement.h> +#include <LibWeb/HTML/HTMLSourceElement.h> +#include <LibWeb/HTML/HTMLSpanElement.h> +#include <LibWeb/HTML/HTMLStyleElement.h> +#include <LibWeb/HTML/HTMLTableCaptionElement.h> +#include <LibWeb/HTML/HTMLTableCellElement.h> +#include <LibWeb/HTML/HTMLTableColElement.h> +#include <LibWeb/HTML/HTMLTableElement.h> +#include <LibWeb/HTML/HTMLTableRowElement.h> +#include <LibWeb/HTML/HTMLTableSectionElement.h> +#include <LibWeb/HTML/HTMLTemplateElement.h> +#include <LibWeb/HTML/HTMLTextAreaElement.h> +#include <LibWeb/HTML/HTMLTimeElement.h> +#include <LibWeb/HTML/HTMLTitleElement.h> +#include <LibWeb/HTML/HTMLTrackElement.h> +#include <LibWeb/HTML/HTMLUListElement.h> +#include <LibWeb/HTML/HTMLUnknownElement.h> +#include <LibWeb/HTML/HTMLVideoElement.h> +#include <LibWeb/SVG/SVGPathElement.h> +#include <LibWeb/SVG/SVGSVGElement.h> + +namespace Web::Bindings { + +NodeWrapper* wrap(JS::GlobalObject& global_object, DOM::Node& node) +{ + if (is<DOM::Document>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<DOM::Document>(node))); + if (is<DOM::DocumentType>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<DOM::DocumentType>(node))); + if (is<HTML::HTMLAnchorElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLAnchorElement>(node))); + if (is<HTML::HTMLAreaElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLAreaElement>(node))); + if (is<HTML::HTMLAudioElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLAudioElement>(node))); + if (is<HTML::HTMLBaseElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLBaseElement>(node))); + if (is<HTML::HTMLBodyElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLBodyElement>(node))); + if (is<HTML::HTMLBRElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLBRElement>(node))); + if (is<HTML::HTMLButtonElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLButtonElement>(node))); + if (is<HTML::HTMLCanvasElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLCanvasElement>(node))); + if (is<HTML::HTMLDataElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLDataElement>(node))); + if (is<HTML::HTMLDataListElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLDataListElement>(node))); + if (is<HTML::HTMLDetailsElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLDetailsElement>(node))); + if (is<HTML::HTMLDialogElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLDialogElement>(node))); + if (is<HTML::HTMLDirectoryElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLDirectoryElement>(node))); + if (is<HTML::HTMLDivElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLDivElement>(node))); + if (is<HTML::HTMLDListElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLDListElement>(node))); + if (is<HTML::HTMLEmbedElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLEmbedElement>(node))); + if (is<HTML::HTMLFieldSetElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLFieldSetElement>(node))); + if (is<HTML::HTMLFontElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLFontElement>(node))); + if (is<HTML::HTMLFormElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLFormElement>(node))); + if (is<HTML::HTMLFrameElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLFrameElement>(node))); + if (is<HTML::HTMLFrameSetElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLFrameSetElement>(node))); + if (is<HTML::HTMLHeadElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLHeadElement>(node))); + if (is<HTML::HTMLHeadingElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLHeadingElement>(node))); + if (is<HTML::HTMLHRElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLHRElement>(node))); + if (is<HTML::HTMLHtmlElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLHtmlElement>(node))); + if (is<HTML::HTMLIFrameElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLIFrameElement>(node))); + if (is<HTML::HTMLImageElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLImageElement>(node))); + if (is<HTML::HTMLInputElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLInputElement>(node))); + if (is<HTML::HTMLLabelElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLLabelElement>(node))); + if (is<HTML::HTMLLegendElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLLegendElement>(node))); + if (is<HTML::HTMLLIElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLLIElement>(node))); + if (is<HTML::HTMLLinkElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLLinkElement>(node))); + if (is<HTML::HTMLMapElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLMapElement>(node))); + if (is<HTML::HTMLMarqueeElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLMarqueeElement>(node))); + if (is<HTML::HTMLMenuElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLMenuElement>(node))); + if (is<HTML::HTMLMetaElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLMetaElement>(node))); + if (is<HTML::HTMLMeterElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLMeterElement>(node))); + if (is<HTML::HTMLModElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLModElement>(node))); + if (is<HTML::HTMLObjectElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLObjectElement>(node))); + if (is<HTML::HTMLOListElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLOListElement>(node))); + if (is<HTML::HTMLOptGroupElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLOptGroupElement>(node))); + if (is<HTML::HTMLOptionElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLOptionElement>(node))); + if (is<HTML::HTMLOutputElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLOutputElement>(node))); + if (is<HTML::HTMLParagraphElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLParagraphElement>(node))); + if (is<HTML::HTMLParamElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLParamElement>(node))); + if (is<HTML::HTMLPictureElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLPictureElement>(node))); + if (is<HTML::HTMLPreElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLPreElement>(node))); + if (is<HTML::HTMLProgressElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLProgressElement>(node))); + if (is<HTML::HTMLQuoteElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLQuoteElement>(node))); + if (is<HTML::HTMLScriptElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLScriptElement>(node))); + if (is<HTML::HTMLSelectElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLSelectElement>(node))); + if (is<HTML::HTMLSlotElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLSlotElement>(node))); + if (is<HTML::HTMLSourceElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLSourceElement>(node))); + if (is<HTML::HTMLSpanElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLSpanElement>(node))); + if (is<HTML::HTMLStyleElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLStyleElement>(node))); + if (is<HTML::HTMLTableCaptionElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTableCaptionElement>(node))); + if (is<HTML::HTMLTableCellElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTableCellElement>(node))); + if (is<HTML::HTMLTableColElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTableColElement>(node))); + if (is<HTML::HTMLTableElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTableElement>(node))); + if (is<HTML::HTMLTableRowElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTableRowElement>(node))); + if (is<HTML::HTMLTableSectionElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTableSectionElement>(node))); + if (is<HTML::HTMLTemplateElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTemplateElement>(node))); + if (is<HTML::HTMLTextAreaElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTextAreaElement>(node))); + if (is<HTML::HTMLTimeElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTimeElement>(node))); + if (is<HTML::HTMLTitleElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTitleElement>(node))); + if (is<HTML::HTMLTrackElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTrackElement>(node))); + if (is<HTML::HTMLUListElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLUListElement>(node))); + if (is<HTML::HTMLUnknownElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLUnknownElement>(node))); + if (is<HTML::HTMLVideoElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLVideoElement>(node))); + if (is<HTML::HTMLElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLElement>(node))); + if (is<SVG::SVGSVGElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<SVG::SVGSVGElement>(node))); + if (is<SVG::SVGPathElement>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<SVG::SVGPathElement>(node))); + if (is<DOM::Element>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<DOM::Element>(node))); + if (is<DOM::DocumentFragment>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<DOM::DocumentFragment>(node))); + if (is<DOM::Comment>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<DOM::Comment>(node))); + if (is<DOM::Text>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<DOM::Text>(node))); + if (is<DOM::CharacterData>(node)) + return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<DOM::CharacterData>(node))); + return static_cast<NodeWrapper*>(wrap_impl(global_object, node)); +} + +} diff --git a/Userland/Libraries/LibWeb/Bindings/NodeWrapperFactory.h b/Userland/Libraries/LibWeb/Bindings/NodeWrapperFactory.h new file mode 100644 index 0000000000..153e78cb32 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/NodeWrapperFactory.h @@ -0,0 +1,38 @@ +/* + * 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 <LibJS/Forward.h> +#include <LibWeb/Forward.h> + +namespace Web { +namespace Bindings { + +NodeWrapper* wrap(JS::GlobalObject&, DOM::Node&); + +} +} diff --git a/Userland/Libraries/LibWeb/Bindings/RangeConstructor.cpp b/Userland/Libraries/LibWeb/Bindings/RangeConstructor.cpp new file mode 100644 index 0000000000..27311c4420 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/RangeConstructor.cpp @@ -0,0 +1,62 @@ +/* + * 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 <LibJS/Heap/Heap.h> +#include <LibWeb/Bindings/RangeConstructor.h> +#include <LibWeb/Bindings/RangePrototype.h> +#include <LibWeb/Bindings/RangeWrapper.h> +#include <LibWeb/Bindings/WindowObject.h> +#include <LibWeb/DOM/Range.h> + +namespace Web::Bindings { + +RangeConstructor::RangeConstructor(JS::GlobalObject& global_object) + : NativeFunction(*global_object.function_prototype()) +{ +} + +void RangeConstructor::initialize(JS::GlobalObject& global_object) +{ + auto& vm = this->vm(); + NativeFunction::initialize(global_object); + auto& window = static_cast<WindowObject&>(global_object); + define_property(vm.names.prototype, window.range_prototype(), 0); + define_property(vm.names.length, JS::Value(0), JS::Attribute::Configurable); +} + +JS::Value RangeConstructor::call() +{ + vm().throw_exception<JS::TypeError>(global_object(), JS::ErrorType::ConstructorWithoutNew, "Range"); + return {}; +} + +JS::Value RangeConstructor::construct(Function&) +{ + auto& window = static_cast<WindowObject&>(global_object()); + return heap().allocate<RangeWrapper>(window, window, DOM::Range::create(window.impl())); +} + +} diff --git a/Userland/Libraries/LibWeb/Bindings/RangeConstructor.h b/Userland/Libraries/LibWeb/Bindings/RangeConstructor.h new file mode 100644 index 0000000000..3189c67bc6 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/RangeConstructor.h @@ -0,0 +1,47 @@ +/* + * 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 <LibJS/Runtime/NativeFunction.h> + +namespace Web::Bindings { + +class RangeConstructor final : public JS::NativeFunction { +public: + explicit RangeConstructor(JS::GlobalObject&); + + void initialize(JS::GlobalObject&) override; + + JS::Value call() override; + JS::Value construct(JS::Function& new_target) override; + +private: + bool has_constructor() const override { return true; } + const char* class_name() const override { return "RangeConstructor"; } +}; + +} diff --git a/Userland/Libraries/LibWeb/Bindings/RangePrototype.cpp b/Userland/Libraries/LibWeb/Bindings/RangePrototype.cpp new file mode 100644 index 0000000000..ed317cb1c6 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/RangePrototype.cpp @@ -0,0 +1,161 @@ +/* + * 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 <AK/Function.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibWeb/Bindings/NodeWrapper.h> +#include <LibWeb/Bindings/NodeWrapperFactory.h> +#include <LibWeb/Bindings/RangePrototype.h> +#include <LibWeb/Bindings/RangeWrapper.h> +#include <LibWeb/DOM/Range.h> + +namespace Web::Bindings { + +RangePrototype::RangePrototype(JS::GlobalObject& global_object) + : Object(*global_object.object_prototype()) +{ +} + +void RangePrototype::initialize(JS::GlobalObject& global_object) +{ + auto default_attributes = JS::Attribute::Enumerable | JS::Attribute::Configurable; + + Object::initialize(global_object); + + define_native_function("setStart", set_start, 2); + define_native_function("setEnd", set_end, 2); + define_native_function("cloneRange", clone_range, 0); + + define_native_property("startContainer", start_container_getter, nullptr, default_attributes); + define_native_property("endContainer", end_container_getter, nullptr, default_attributes); + define_native_property("startOffset", start_offset_getter, nullptr, default_attributes); + define_native_property("endOffset", end_offset_getter, nullptr, default_attributes); +} + +static DOM::Range* impl_from(JS::VM& vm, JS::GlobalObject& global_object) +{ + auto* this_object = vm.this_value(global_object).to_object(global_object); + if (!this_object) + return nullptr; + if (StringView("RangeWrapper") != this_object->class_name()) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Range"); + return nullptr; + } + return &static_cast<RangeWrapper*>(this_object)->impl(); +} + +JS_DEFINE_NATIVE_FUNCTION(RangePrototype::set_start) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + + auto arg0 = vm.argument(0).to_object(global_object); + if (vm.exception()) + return {}; + auto arg1 = vm.argument(1).to_number(global_object); + if (vm.exception()) + return {}; + + if (!is<NodeWrapper>(arg0)) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Node"); + return {}; + } + + impl->set_start(static_cast<NodeWrapper*>(arg0)->impl(), arg1.as_i32()); + + return JS::js_undefined(); +} + +JS_DEFINE_NATIVE_FUNCTION(RangePrototype::set_end) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + + auto arg0 = vm.argument(0).to_object(global_object); + if (vm.exception()) + return {}; + auto arg1 = vm.argument(1).to_number(global_object); + if (vm.exception()) + return {}; + + if (!is<NodeWrapper>(arg0)) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Node"); + return {}; + } + + impl->set_end(static_cast<NodeWrapper*>(arg0)->impl(), arg1.as_i32()); + + return JS::js_undefined(); +} + +JS_DEFINE_NATIVE_FUNCTION(RangePrototype::clone_range) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + + return wrap(global_object, *impl->clone_range()); +} + +JS_DEFINE_NATIVE_GETTER(RangePrototype::start_container_getter) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + + return wrap(global_object, *impl->start_container()); +} + +JS_DEFINE_NATIVE_GETTER(RangePrototype::end_container_getter) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + + return wrap(global_object, *impl->end_container()); +} + +JS_DEFINE_NATIVE_GETTER(RangePrototype::start_offset_getter) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + + return JS::Value(impl->start_offset()); +} + +JS_DEFINE_NATIVE_GETTER(RangePrototype::end_offset_getter) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + + return JS::Value(impl->end_offset()); +} + +} diff --git a/Userland/Libraries/LibWeb/Bindings/RangePrototype.h b/Userland/Libraries/LibWeb/Bindings/RangePrototype.h new file mode 100644 index 0000000000..560eaa4a7c --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/RangePrototype.h @@ -0,0 +1,53 @@ +/* + * 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 <LibJS/Runtime/Object.h> + +namespace Web::Bindings { + +class RangePrototype final : public JS::Object { + JS_OBJECT(RangePrototype, JS::Object); + +public: + explicit RangePrototype(JS::GlobalObject&); + + void initialize(JS::GlobalObject&) override; + +private: + JS_DECLARE_NATIVE_FUNCTION(set_start); + JS_DECLARE_NATIVE_FUNCTION(set_end); + JS_DECLARE_NATIVE_FUNCTION(clone_range); + + JS_DECLARE_NATIVE_GETTER(start_container_getter); + JS_DECLARE_NATIVE_GETTER(end_container_getter); + JS_DECLARE_NATIVE_GETTER(start_offset_getter); + JS_DECLARE_NATIVE_GETTER(end_offset_getter); + JS_DECLARE_NATIVE_GETTER(collapsed_getter); +}; + +} diff --git a/Userland/Libraries/LibWeb/Bindings/RangeWrapper.cpp b/Userland/Libraries/LibWeb/Bindings/RangeWrapper.cpp new file mode 100644 index 0000000000..dc58a4358d --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/RangeWrapper.cpp @@ -0,0 +1,47 @@ +/* + * 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 <LibWeb/Bindings/RangePrototype.h> +#include <LibWeb/Bindings/RangeWrapper.h> +#include <LibWeb/Bindings/WindowObject.h> +#include <LibWeb/Bindings/Wrappable.h> +#include <LibWeb/DOM/Range.h> + +namespace Web::Bindings { + +RangeWrapper::RangeWrapper(JS::GlobalObject& global_object, DOM::Range& impl) + : Wrapper(global_object) + , m_impl(impl) +{ + set_prototype(static_cast<WindowObject&>(global_object).range_prototype()); +} + +RangeWrapper* wrap(JS::GlobalObject& global_object, DOM::Range& impl) +{ + return static_cast<RangeWrapper*>(wrap_impl(global_object, impl)); +} + +} diff --git a/Userland/Libraries/LibWeb/Bindings/RangeWrapper.h b/Userland/Libraries/LibWeb/Bindings/RangeWrapper.h new file mode 100644 index 0000000000..5528d81911 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/RangeWrapper.h @@ -0,0 +1,48 @@ +/* + * 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 <LibWeb/Bindings/Wrapper.h> + +namespace Web::Bindings { + +class RangeWrapper final : public Wrapper { +public: + RangeWrapper(JS::GlobalObject&, DOM::Range&); + + DOM::Range& impl() { return m_impl; } + const DOM::Range& impl() const { return m_impl; } + +private: + virtual const char* class_name() const override { return "RangeWrapper"; } + + NonnullRefPtr<DOM::Range> m_impl; +}; + +RangeWrapper* wrap(JS::GlobalObject&, DOM::Range&); + +} diff --git a/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.cpp b/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.cpp new file mode 100644 index 0000000000..ea7e58864e --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.cpp @@ -0,0 +1,35 @@ +/* + * 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 <LibWeb/Bindings/ScriptExecutionContext.h> + +namespace Web::Bindings { + +ScriptExecutionContext::~ScriptExecutionContext() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.h b/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.h new file mode 100644 index 0000000000..1c21910741 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.h @@ -0,0 +1,43 @@ +/* + * 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 <AK/Weakable.h> +#include <LibJS/Forward.h> +#include <LibWeb/Forward.h> + +namespace Web::Bindings { + +class ScriptExecutionContext { +public: + virtual ~ScriptExecutionContext(); + + // FIXME: This should not work this way long-term, interpreters should be on the stack. + virtual JS::Interpreter& interpreter() = 0; +}; + +} diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObject.cpp b/Userland/Libraries/LibWeb/Bindings/WindowObject.cpp new file mode 100644 index 0000000000..f147a86bf0 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/WindowObject.cpp @@ -0,0 +1,361 @@ +/* + * 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/Base64.h> +#include <AK/ByteBuffer.h> +#include <AK/FlyString.h> +#include <AK/Function.h> +#include <AK/String.h> +#include <AK/Utf8View.h> +#include <LibJS/Runtime/Error.h> +#include <LibJS/Runtime/Function.h> +#include <LibJS/Runtime/Shape.h> +#include <LibTextCodec/Decoder.h> +#include <LibWeb/Bindings/DocumentWrapper.h> +#include <LibWeb/Bindings/EventWrapper.h> +#include <LibWeb/Bindings/EventWrapperFactory.h> +#include <LibWeb/Bindings/LocationObject.h> +#include <LibWeb/Bindings/NavigatorObject.h> +#include <LibWeb/Bindings/NodeWrapperFactory.h> +#include <LibWeb/Bindings/PerformanceWrapper.h> +#include <LibWeb/Bindings/RangeConstructor.h> +#include <LibWeb/Bindings/RangePrototype.h> +#include <LibWeb/Bindings/WindowObject.h> +#include <LibWeb/Bindings/XMLHttpRequestConstructor.h> +#include <LibWeb/Bindings/XMLHttpRequestPrototype.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/DOM/Window.h> +#include <LibWeb/Origin.h> + +namespace Web { +namespace Bindings { + +WindowObject::WindowObject(DOM::Window& impl) + : m_impl(impl) +{ + impl.set_wrapper({}, *this); +} + +void WindowObject::initialize() +{ + GlobalObject::initialize(); + + define_property("window", this, JS::Attribute::Enumerable); + define_property("frames", this, JS::Attribute::Enumerable); + define_property("self", this, JS::Attribute::Enumerable); + define_native_property("document", document_getter, document_setter, JS::Attribute::Enumerable); + define_native_property("performance", performance_getter, nullptr, JS::Attribute::Enumerable); + define_native_function("alert", alert); + define_native_function("confirm", confirm); + define_native_function("setInterval", set_interval, 1); + define_native_function("setTimeout", set_timeout, 1); + define_native_function("clearInterval", clear_interval, 1); + define_native_function("clearTimeout", clear_timeout, 1); + define_native_function("requestAnimationFrame", request_animation_frame, 1); + define_native_function("cancelAnimationFrame", cancel_animation_frame, 1); + define_native_function("atob", atob, 1); + define_native_function("btoa", btoa, 1); + + // Legacy + define_native_property("event", event_getter, nullptr, JS::Attribute::Enumerable); + + define_property("navigator", heap().allocate<NavigatorObject>(*this, *this), JS::Attribute::Enumerable | JS::Attribute::Configurable); + define_property("location", heap().allocate<LocationObject>(*this, *this), JS::Attribute::Enumerable | JS::Attribute::Configurable); + + m_xhr_prototype = heap().allocate<XMLHttpRequestPrototype>(*this, *this); + add_constructor("XMLHttpRequest", m_xhr_constructor, m_xhr_prototype); + + m_range_prototype = heap().allocate<RangePrototype>(*this, *this); + add_constructor("Range", m_range_constructor, m_range_prototype); +} + +WindowObject::~WindowObject() +{ +} + +void WindowObject::visit_edges(Visitor& visitor) +{ + GlobalObject::visit_edges(visitor); + visitor.visit(m_xhr_constructor); + visitor.visit(m_xhr_prototype); + visitor.visit(m_range_constructor); + visitor.visit(m_range_prototype); +} + +Origin WindowObject::origin() const +{ + return impl().document().origin(); +} + +static DOM::Window* impl_from(JS::VM& vm, JS::GlobalObject& global_object) +{ + auto* this_object = vm.this_value(global_object).to_object(global_object); + if (!this_object) { + ASSERT_NOT_REACHED(); + return nullptr; + } + if (StringView("WindowObject") != this_object->class_name()) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "WindowObject"); + return nullptr; + } + return &static_cast<WindowObject*>(this_object)->impl(); +} + +JS_DEFINE_NATIVE_FUNCTION(WindowObject::alert) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + String message = ""; + if (vm.argument_count()) { + message = vm.argument(0).to_string(global_object); + if (vm.exception()) + return {}; + } + impl->alert(message); + return JS::js_undefined(); +} + +JS_DEFINE_NATIVE_FUNCTION(WindowObject::confirm) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + String message = ""; + if (vm.argument_count()) { + message = vm.argument(0).to_string(global_object); + if (vm.exception()) + return {}; + } + return JS::Value(impl->confirm(message)); +} + +JS_DEFINE_NATIVE_FUNCTION(WindowObject::set_interval) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + if (!vm.argument_count()) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountAtLeastOne, "setInterval"); + return {}; + } + auto* callback_object = vm.argument(0).to_object(global_object); + if (!callback_object) + return {}; + if (!callback_object->is_function()) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAFunctionNoParam); + return {}; + } + i32 interval = 0; + if (vm.argument_count() >= 2) { + interval = vm.argument(1).to_i32(global_object); + if (vm.exception()) + return {}; + if (interval < 0) + interval = 0; + } + + auto timer_id = impl->set_interval(*static_cast<JS::Function*>(callback_object), interval); + return JS::Value(timer_id); +} + +JS_DEFINE_NATIVE_FUNCTION(WindowObject::set_timeout) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + if (!vm.argument_count()) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountAtLeastOne, "setTimeout"); + return {}; + } + auto* callback_object = vm.argument(0).to_object(global_object); + if (!callback_object) + return {}; + if (!callback_object->is_function()) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAFunctionNoParam); + return {}; + } + i32 interval = 0; + if (vm.argument_count() >= 2) { + interval = vm.argument(1).to_i32(global_object); + if (vm.exception()) + return {}; + if (interval < 0) + interval = 0; + } + + auto timer_id = impl->set_timeout(*static_cast<JS::Function*>(callback_object), interval); + return JS::Value(timer_id); +} + +JS_DEFINE_NATIVE_FUNCTION(WindowObject::clear_timeout) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + if (!vm.argument_count()) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountAtLeastOne, "clearTimeout"); + return {}; + } + i32 timer_id = vm.argument(0).to_i32(global_object); + if (vm.exception()) + return {}; + impl->clear_timeout(timer_id); + return JS::js_undefined(); +} + +JS_DEFINE_NATIVE_FUNCTION(WindowObject::clear_interval) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + if (!vm.argument_count()) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountAtLeastOne, "clearInterval"); + return {}; + } + i32 timer_id = vm.argument(0).to_i32(global_object); + if (vm.exception()) + return {}; + impl->clear_timeout(timer_id); + return JS::js_undefined(); +} + +JS_DEFINE_NATIVE_FUNCTION(WindowObject::request_animation_frame) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + if (!vm.argument_count()) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountOne, "requestAnimationFrame"); + return {}; + } + auto* callback_object = vm.argument(0).to_object(global_object); + if (!callback_object) + return {}; + if (!callback_object->is_function()) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAFunctionNoParam); + return {}; + } + return JS::Value(impl->request_animation_frame(*static_cast<JS::Function*>(callback_object))); +} + +JS_DEFINE_NATIVE_FUNCTION(WindowObject::cancel_animation_frame) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + if (!vm.argument_count()) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountOne, "cancelAnimationFrame"); + return {}; + } + auto id = vm.argument(0).to_i32(global_object); + if (vm.exception()) + return {}; + impl->cancel_animation_frame(id); + return JS::js_undefined(); +} + +JS_DEFINE_NATIVE_FUNCTION(WindowObject::atob) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + if (!vm.argument_count()) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountOne, "atob"); + return {}; + } + auto string = vm.argument(0).to_string(global_object); + if (vm.exception()) + return {}; + auto decoded = decode_base64(StringView(string)); + + // decode_base64() returns a byte string. LibJS uses UTF-8 for strings. Use Latin1Decoder to convert bytes 128-255 to UTF-8. + auto decoder = TextCodec::decoder_for("windows-1252"); + ASSERT(decoder); + return JS::js_string(vm, decoder->to_utf8(decoded)); +} + +JS_DEFINE_NATIVE_FUNCTION(WindowObject::btoa) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + if (!vm.argument_count()) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountOne, "btoa"); + return {}; + } + auto string = vm.argument(0).to_string(global_object); + if (vm.exception()) + return {}; + + Vector<u8> byte_string; + byte_string.ensure_capacity(string.length()); + for (u32 code_point : Utf8View(string)) { + if (code_point > 0xff) { + vm.throw_exception<JS::InvalidCharacterError>(global_object, JS::ErrorType::NotAByteString, "btoa"); + return {}; + } + byte_string.append(code_point); + } + + auto encoded = encode_base64(byte_string.span()); + return JS::js_string(vm, move(encoded)); +} + +JS_DEFINE_NATIVE_GETTER(WindowObject::document_getter) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + return wrap(global_object, impl->document()); +} + +JS_DEFINE_NATIVE_SETTER(WindowObject::document_setter) +{ + // FIXME: Figure out what we should do here. Just ignore attempts to set window.document for now. +} + +JS_DEFINE_NATIVE_GETTER(WindowObject::performance_getter) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + return wrap(global_object, impl->performance()); +} + +JS_DEFINE_NATIVE_GETTER(WindowObject::event_getter) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + if (!impl->current_event()) + return JS::js_undefined(); + return wrap(global_object, const_cast<DOM::Event&>(*impl->current_event())); +} + +} +} diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObject.h b/Userland/Libraries/LibWeb/Bindings/WindowObject.h new file mode 100644 index 0000000000..d68a3364c9 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/WindowObject.h @@ -0,0 +1,88 @@ +/* + * 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 <AK/TypeCasts.h> +#include <AK/Weakable.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibWeb/Forward.h> + +namespace Web { +namespace Bindings { + +class WindowObject final + : public JS::GlobalObject + , public Weakable<WindowObject> { +public: + explicit WindowObject(DOM::Window&); + virtual void initialize() override; + virtual ~WindowObject() override; + + DOM::Window& impl() { return *m_impl; } + const DOM::Window& impl() const { return *m_impl; } + + Origin origin() const; + + XMLHttpRequestPrototype* xhr_prototype() { return m_xhr_prototype; } + XMLHttpRequestConstructor* xhr_constructor() { return m_xhr_constructor; } + + RangePrototype* range_prototype() { return m_range_prototype; } + RangeConstructor* range_constructor() { return m_range_constructor; } + +private: + virtual const char* class_name() const override { return "WindowObject"; } + virtual void visit_edges(Visitor&) override; + + JS_DECLARE_NATIVE_GETTER(document_getter); + JS_DECLARE_NATIVE_SETTER(document_setter); + + JS_DECLARE_NATIVE_GETTER(performance_getter); + + JS_DECLARE_NATIVE_GETTER(event_getter); + + JS_DECLARE_NATIVE_FUNCTION(alert); + JS_DECLARE_NATIVE_FUNCTION(confirm); + JS_DECLARE_NATIVE_FUNCTION(set_interval); + JS_DECLARE_NATIVE_FUNCTION(set_timeout); + JS_DECLARE_NATIVE_FUNCTION(clear_interval); + JS_DECLARE_NATIVE_FUNCTION(clear_timeout); + JS_DECLARE_NATIVE_FUNCTION(request_animation_frame); + JS_DECLARE_NATIVE_FUNCTION(cancel_animation_frame); + JS_DECLARE_NATIVE_FUNCTION(atob); + JS_DECLARE_NATIVE_FUNCTION(btoa); + + NonnullRefPtr<DOM::Window> m_impl; + + XMLHttpRequestConstructor* m_xhr_constructor { nullptr }; + XMLHttpRequestPrototype* m_xhr_prototype { nullptr }; + + RangePrototype* m_range_prototype { nullptr }; + RangeConstructor* m_range_constructor { nullptr }; +}; + +} +} diff --git a/Userland/Libraries/LibWeb/Bindings/Wrappable.cpp b/Userland/Libraries/LibWeb/Bindings/Wrappable.cpp new file mode 100644 index 0000000000..b257a378a6 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/Wrappable.cpp @@ -0,0 +1,44 @@ +/* + * 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 <LibWeb/Bindings/Wrappable.h> +#include <LibWeb/Bindings/Wrapper.h> + +namespace Web { +namespace Bindings { + +Wrappable::~Wrappable() +{ +} + +void Wrappable::set_wrapper(Wrapper& wrapper) +{ + ASSERT(!m_wrapper); + m_wrapper = wrapper.make_weak_ptr(); +} + +} +} diff --git a/Userland/Libraries/LibWeb/Bindings/Wrappable.h b/Userland/Libraries/LibWeb/Bindings/Wrappable.h new file mode 100644 index 0000000000..211fc4755a --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/Wrappable.h @@ -0,0 +1,57 @@ +/* + * 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 <AK/WeakPtr.h> +#include <LibJS/Heap/Heap.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibWeb/Forward.h> + +namespace Web::Bindings { + +class Wrappable { +public: + virtual ~Wrappable(); + + void set_wrapper(Wrapper&); + Wrapper* wrapper() { return m_wrapper; } + const Wrapper* wrapper() const { return m_wrapper; } + +private: + WeakPtr<Wrapper> m_wrapper; +}; + +template<class NativeObject> +inline Wrapper* wrap_impl(JS::GlobalObject& global_object, NativeObject& native_object) +{ + if (!native_object.wrapper()) { + native_object.set_wrapper(*global_object.heap().allocate<typename NativeObject::WrapperType>(global_object, global_object, native_object)); + } + return native_object.wrapper(); +} + +} diff --git a/Userland/Libraries/LibWeb/Bindings/Wrapper.h b/Userland/Libraries/LibWeb/Bindings/Wrapper.h new file mode 100644 index 0000000000..c83556605a --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/Wrapper.h @@ -0,0 +1,49 @@ +/* + * 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 <AK/NonnullRefPtr.h> +#include <AK/Weakable.h> +#include <LibJS/Runtime/Object.h> +#include <LibWeb/Forward.h> + +namespace Web::Bindings { + +class Wrapper + : public JS::Object + , public Weakable<Wrapper> { + JS_OBJECT(Wrapper, JS::Object); + +public: +protected: + explicit Wrapper(Object& prototype) + : Object(prototype) + { + } +}; + +} diff --git a/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestConstructor.cpp b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestConstructor.cpp new file mode 100644 index 0000000000..16bb2a4b8b --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestConstructor.cpp @@ -0,0 +1,73 @@ +/* + * 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 <LibJS/Heap/Heap.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibWeb/Bindings/WindowObject.h> +#include <LibWeb/Bindings/XMLHttpRequestConstructor.h> +#include <LibWeb/Bindings/XMLHttpRequestPrototype.h> +#include <LibWeb/Bindings/XMLHttpRequestWrapper.h> +#include <LibWeb/DOM/XMLHttpRequest.h> + +namespace Web::Bindings { + +XMLHttpRequestConstructor::XMLHttpRequestConstructor(JS::GlobalObject& global_object) + : NativeFunction(*global_object.function_prototype()) +{ +} + +void XMLHttpRequestConstructor::initialize(JS::GlobalObject& global_object) +{ + auto& vm = this->vm(); + NativeFunction::initialize(global_object); + auto& window = static_cast<WindowObject&>(global_object); + define_property(vm.names.prototype, window.xhr_prototype(), 0); + define_property(vm.names.length, JS::Value(1), JS::Attribute::Configurable); + + define_property("UNSENT", JS::Value((i32)XMLHttpRequest::ReadyState::Unsent), JS::Attribute::Enumerable); + define_property("OPENED", JS::Value((i32)XMLHttpRequest::ReadyState::Opened), JS::Attribute::Enumerable); + define_property("HEADERS_RECEIVED", JS::Value((i32)XMLHttpRequest::ReadyState::HeadersReceived), JS::Attribute::Enumerable); + define_property("LOADING", JS::Value((i32)XMLHttpRequest::ReadyState::Loading), JS::Attribute::Enumerable); + define_property("DONE", JS::Value((i32)XMLHttpRequest::ReadyState::Done), JS::Attribute::Enumerable); +} + +XMLHttpRequestConstructor::~XMLHttpRequestConstructor() +{ +} + +JS::Value XMLHttpRequestConstructor::call() +{ + vm().throw_exception<JS::TypeError>(global_object(), JS::ErrorType::ConstructorWithoutNew, "XMLHttpRequest"); + return {}; +} + +JS::Value XMLHttpRequestConstructor::construct(Function&) +{ + auto& window = static_cast<WindowObject&>(global_object()); + return heap().allocate<XMLHttpRequestWrapper>(window, window, XMLHttpRequest::create(window.impl())); +} + +} diff --git a/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestConstructor.h b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestConstructor.h new file mode 100644 index 0000000000..0ca6f5063f --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestConstructor.h @@ -0,0 +1,47 @@ +/* + * 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 <LibJS/Runtime/NativeFunction.h> + +namespace Web::Bindings { + +class XMLHttpRequestConstructor final : public JS::NativeFunction { +public: + explicit XMLHttpRequestConstructor(JS::GlobalObject&); + virtual void initialize(JS::GlobalObject&) override; + virtual ~XMLHttpRequestConstructor() override; + + virtual JS::Value call() override; + virtual JS::Value construct(JS::Function& new_target) override; + +private: + virtual bool has_constructor() const override { return true; } + virtual const char* class_name() const override { return "XMLHttpRequestConstructor"; } +}; + +} diff --git a/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.cpp b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.cpp new file mode 100644 index 0000000000..2db588bc2c --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.cpp @@ -0,0 +1,112 @@ +/* + * 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/Function.h> +#include <LibJS/Runtime/Error.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibWeb/Bindings/XMLHttpRequestPrototype.h> +#include <LibWeb/Bindings/XMLHttpRequestWrapper.h> +#include <LibWeb/DOM/XMLHttpRequest.h> + +namespace Web::Bindings { + +XMLHttpRequestPrototype::XMLHttpRequestPrototype(JS::GlobalObject& global_object) + : Object(*global_object.object_prototype()) +{ +} + +void XMLHttpRequestPrototype::initialize(JS::GlobalObject& global_object) +{ + Object::initialize(global_object); + define_native_function("open", open, 2); + define_native_function("send", send, 0); + define_native_property("readyState", ready_state_getter, nullptr, JS::Attribute::Enumerable | JS::Attribute::Configurable); + define_native_property("responseText", response_text_getter, nullptr, JS::Attribute::Enumerable | JS::Attribute::Configurable); + + define_property("UNSENT", JS::Value((i32)XMLHttpRequest::ReadyState::Unsent), JS::Attribute::Enumerable); + define_property("OPENED", JS::Value((i32)XMLHttpRequest::ReadyState::Opened), JS::Attribute::Enumerable); + define_property("HEADERS_RECEIVED", JS::Value((i32)XMLHttpRequest::ReadyState::HeadersReceived), JS::Attribute::Enumerable); + define_property("LOADING", JS::Value((i32)XMLHttpRequest::ReadyState::Loading), JS::Attribute::Enumerable); + define_property("DONE", JS::Value((i32)XMLHttpRequest::ReadyState::Done), JS::Attribute::Enumerable); +} + +XMLHttpRequestPrototype::~XMLHttpRequestPrototype() +{ +} + +static XMLHttpRequest* impl_from(JS::VM& vm, JS::GlobalObject& global_object) +{ + auto* this_object = vm.this_value(global_object).to_object(global_object); + if (!this_object) + return nullptr; + if (!is<XMLHttpRequestWrapper>(this_object)) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "XMLHttpRequest"); + return nullptr; + } + return &static_cast<XMLHttpRequestWrapper*>(this_object)->impl(); +} + +JS_DEFINE_NATIVE_FUNCTION(XMLHttpRequestPrototype::open) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + auto arg0 = vm.argument(0).to_string(global_object); + if (vm.exception()) + return {}; + auto arg1 = vm.argument(1).to_string(global_object); + if (vm.exception()) + return {}; + impl->open(arg0, arg1); + return JS::js_undefined(); +} + +JS_DEFINE_NATIVE_FUNCTION(XMLHttpRequestPrototype::send) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + impl->send(); + return JS::js_undefined(); +} + +JS_DEFINE_NATIVE_GETTER(XMLHttpRequestPrototype::ready_state_getter) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + return JS::Value((i32)impl->ready_state()); +} + +JS_DEFINE_NATIVE_GETTER(XMLHttpRequestPrototype::response_text_getter) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; + return JS::js_string(vm, impl->response_text()); +} + +} diff --git a/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.h b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.h new file mode 100644 index 0000000000..7b534b3b00 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.h @@ -0,0 +1,49 @@ +/* + * 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 <LibJS/Runtime/Object.h> + +namespace Web::Bindings { + +class XMLHttpRequestPrototype final : public JS::Object { + JS_OBJECT(XMLHttpRequestPrototype, JS::Object); + +public: + explicit XMLHttpRequestPrototype(JS::GlobalObject&); + virtual void initialize(JS::GlobalObject&) override; + virtual ~XMLHttpRequestPrototype() override; + +private: + JS_DECLARE_NATIVE_FUNCTION(open); + JS_DECLARE_NATIVE_FUNCTION(send); + + JS_DECLARE_NATIVE_GETTER(ready_state_getter); + JS_DECLARE_NATIVE_GETTER(response_text_getter); +}; + +} diff --git a/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestWrapper.cpp b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestWrapper.cpp new file mode 100644 index 0000000000..dedd97313a --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestWrapper.cpp @@ -0,0 +1,62 @@ +/* + * 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 <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/Value.h> +#include <LibWeb/Bindings/WindowObject.h> +#include <LibWeb/Bindings/XMLHttpRequestPrototype.h> +#include <LibWeb/Bindings/XMLHttpRequestWrapper.h> +#include <LibWeb/DOM/XMLHttpRequest.h> + +namespace Web::Bindings { + +XMLHttpRequestWrapper* wrap(JS::GlobalObject& global_object, XMLHttpRequest& impl) +{ + return static_cast<XMLHttpRequestWrapper*>(wrap_impl(global_object, impl)); +} + +XMLHttpRequestWrapper::XMLHttpRequestWrapper(JS::GlobalObject& global_object, XMLHttpRequest& impl) + : EventTargetWrapper(global_object, impl) +{ + set_prototype(static_cast<WindowObject&>(global_object).xhr_prototype()); +} + +XMLHttpRequestWrapper::~XMLHttpRequestWrapper() +{ +} + +XMLHttpRequest& XMLHttpRequestWrapper::impl() +{ + return static_cast<XMLHttpRequest&>(EventTargetWrapper::impl()); +} + +const XMLHttpRequest& XMLHttpRequestWrapper::impl() const +{ + return static_cast<const XMLHttpRequest&>(EventTargetWrapper::impl()); +} + +} diff --git a/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestWrapper.h b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestWrapper.h new file mode 100644 index 0000000000..a961564b60 --- /dev/null +++ b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestWrapper.h @@ -0,0 +1,47 @@ +/* + * 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/EventTargetWrapper.h> + +namespace Web::Bindings { + +class XMLHttpRequestWrapper final : public EventTargetWrapper { +public: + XMLHttpRequestWrapper(JS::GlobalObject&, XMLHttpRequest&); + virtual ~XMLHttpRequestWrapper() override; + + XMLHttpRequest& impl(); + const XMLHttpRequest& impl() const; + +private: + virtual const char* class_name() const override { return "XMLHttpRequestWrapper"; } +}; + +XMLHttpRequestWrapper* wrap(JS::GlobalObject&, XMLHttpRequest&); + +} diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt new file mode 100644 index 0000000000..4e4abc1551 --- /dev/null +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -0,0 +1,399 @@ +set(SOURCES + Bindings/EventListenerWrapper.cpp + Bindings/EventWrapperFactory.cpp + Bindings/EventTargetWrapperFactory.cpp + Bindings/LocationObject.cpp + Bindings/NavigatorObject.cpp + Bindings/NodeWrapperFactory.cpp + Bindings/ScriptExecutionContext.cpp + Bindings/WindowObject.cpp + Bindings/Wrappable.cpp + Bindings/XMLHttpRequestConstructor.cpp + Bindings/XMLHttpRequestPrototype.cpp + Bindings/XMLHttpRequestWrapper.cpp + Bindings/RangeConstructor.cpp + Bindings/RangePrototype.cpp + Bindings/RangeWrapper.cpp + CSS/DefaultStyleSheetSource.cpp + CSS/Length.cpp + CSS/Parser/CSSParser.cpp + CSS/PropertyID.cpp + CSS/PropertyID.h + CSS/QuirksModeStyleSheetSource.cpp + CSS/Selector.cpp + CSS/SelectorEngine.cpp + CSS/StyleDeclaration.cpp + CSS/StyleInvalidator.cpp + CSS/StyleProperties.cpp + CSS/StyleResolver.cpp + CSS/StyleRule.cpp + CSS/StyleSheet.cpp + CSS/StyleSheetList.cpp + CSS/StyleValue.cpp + CSS/ValueID.cpp + CSS/ValueID.h + DOM/CharacterData.cpp + DOM/CharacterData.idl + DOM/Comment.cpp + DOM/Document.cpp + DOM/DocumentFragment.cpp + DOM/DocumentType.cpp + DOM/DOMImplementation.cpp + DOM/Element.cpp + DOM/ElementFactory.cpp + DOM/Event.cpp + DOM/Range.cpp + DOM/EventDispatcher.cpp + DOM/EventListener.cpp + DOM/EventTarget.cpp + DOM/Node.cpp + DOM/ParentNode.cpp + DOM/Position.cpp + DOM/ShadowRoot.cpp + DOM/Text.cpp + DOM/Text.idl + DOM/Timer.cpp + DOM/Window.cpp + DOM/XMLHttpRequest.cpp + DOMTreeModel.cpp + Dump.cpp + FontCache.cpp + HTML/AttributeNames.cpp + HTML/CanvasRenderingContext2D.cpp + HTML/EventNames.cpp + HTML/HTMLAnchorElement.cpp + HTML/HTMLAreaElement.cpp + HTML/HTMLAudioElement.cpp + HTML/HTMLBRElement.cpp + HTML/HTMLBaseElement.cpp + HTML/HTMLBlinkElement.cpp + HTML/HTMLBodyElement.cpp + HTML/HTMLButtonElement.cpp + HTML/HTMLCanvasElement.cpp + HTML/HTMLDListElement.cpp + HTML/HTMLDataElement.cpp + HTML/HTMLDataListElement.cpp + HTML/HTMLDetailsElement.cpp + HTML/HTMLDialogElement.cpp + HTML/HTMLDirectoryElement.cpp + HTML/HTMLDivElement.cpp + HTML/HTMLElement.cpp + HTML/HTMLEmbedElement.cpp + HTML/HTMLFieldSetElement.cpp + HTML/HTMLFontElement.cpp + HTML/HTMLFormElement.cpp + HTML/HTMLFrameElement.cpp + HTML/HTMLFrameSetElement.cpp + HTML/HTMLHRElement.cpp + HTML/HTMLHeadElement.cpp + HTML/HTMLHeadingElement.cpp + HTML/HTMLHtmlElement.cpp + HTML/HTMLIFrameElement.cpp + HTML/HTMLImageElement.cpp + HTML/HTMLInputElement.cpp + HTML/HTMLLIElement.cpp + HTML/HTMLLabelElement.cpp + HTML/HTMLLegendElement.cpp + HTML/HTMLLinkElement.cpp + HTML/HTMLMapElement.cpp + HTML/HTMLMarqueeElement.cpp + HTML/HTMLMediaElement.cpp + HTML/HTMLMenuElement.cpp + HTML/HTMLMetaElement.cpp + HTML/HTMLMeterElement.cpp + HTML/HTMLModElement.cpp + HTML/HTMLOListElement.cpp + HTML/HTMLObjectElement.cpp + HTML/HTMLOptGroupElement.cpp + HTML/HTMLOptionElement.cpp + HTML/HTMLOutputElement.cpp + HTML/HTMLParagraphElement.cpp + HTML/HTMLParamElement.cpp + HTML/HTMLPictureElement.cpp + HTML/HTMLPreElement.cpp + HTML/HTMLProgressElement.cpp + HTML/HTMLQuoteElement.cpp + HTML/HTMLScriptElement.cpp + HTML/HTMLSelectElement.cpp + HTML/HTMLSlotElement.cpp + HTML/HTMLSourceElement.cpp + HTML/HTMLSpanElement.cpp + HTML/HTMLStyleElement.cpp + HTML/HTMLTableCaptionElement.cpp + HTML/HTMLTableCellElement.cpp + HTML/HTMLTableColElement.cpp + HTML/HTMLTableElement.cpp + HTML/HTMLTableRowElement.cpp + HTML/HTMLTableSectionElement.cpp + HTML/HTMLTemplateElement.cpp + HTML/HTMLTextAreaElement.cpp + HTML/HTMLTimeElement.cpp + HTML/HTMLTitleElement.cpp + HTML/HTMLTrackElement.cpp + HTML/HTMLUListElement.cpp + HTML/HTMLUnknownElement.cpp + HTML/HTMLVideoElement.cpp + HTML/ImageData.cpp + HTML/Parser/Entities.cpp + HTML/Parser/HTMLDocumentParser.cpp + HTML/Parser/HTMLToken.cpp + HTML/Parser/HTMLTokenizer.cpp + HTML/Parser/ListOfActiveFormattingElements.cpp + HTML/Parser/StackOfOpenElements.cpp + HTML/TagNames.cpp + HighResolutionTime/Performance.cpp + InProcessWebView.cpp + Layout/BlockBox.cpp + Layout/BlockFormattingContext.cpp + Layout/Box.cpp + Layout/BoxModelMetrics.cpp + Layout/BreakNode.cpp + Layout/ButtonBox.cpp + Layout/CanvasBox.cpp + Layout/CheckBox.cpp + Layout/FormattingContext.cpp + Layout/FrameBox.cpp + Layout/ImageBox.cpp + Layout/InitialContainingBlockBox.cpp + Layout/InlineFormattingContext.cpp + Layout/InlineNode.cpp + Layout/LayoutPosition.cpp + Layout/LineBox.cpp + Layout/LineBoxFragment.cpp + Layout/ListItemBox.cpp + Layout/ListItemMarkerBox.cpp + Layout/Node.cpp + Layout/ReplacedBox.cpp + Layout/SVGBox.cpp + Layout/SVGGraphicsBox.cpp + Layout/SVGPathBox.cpp + Layout/SVGSVGBox.cpp + Layout/TableBox.cpp + Layout/TableCellBox.cpp + Layout/TableFormattingContext.cpp + Layout/TableRowBox.cpp + Layout/TableRowGroupBox.cpp + Layout/TextNode.cpp + Layout/TreeBuilder.cpp + Layout/WidgetBox.cpp + LayoutTreeModel.cpp + Loader/ContentFilter.cpp + Loader/FrameLoader.cpp + Loader/ImageLoader.cpp + Loader/ImageResource.cpp + Loader/Resource.cpp + Loader/ResourceLoader.cpp + Namespace.cpp + OutOfProcessWebView.cpp + Page/EventHandler.cpp + Page/EditEventHandler.cpp + Page/Frame.cpp + Page/Page.cpp + Painting/BorderPainting.cpp + Painting/StackingContext.cpp + SVG/SVGElement.cpp + SVG/SVGGeometryElement.cpp + SVG/SVGGraphicsElement.cpp + SVG/SVGPathElement.cpp + SVG/SVGSVGElement.cpp + SVG/TagNames.cpp + StylePropertiesModel.cpp + UIEvents/EventNames.cpp + UIEvents/MouseEvent.cpp + URLEncoder.cpp + WebContentClient.cpp +) + +set(GENERATED_SOURCES + ../../Services/ProtocolServer/ProtocolClientEndpoint.h + ../../Services/ProtocolServer/ProtocolServerEndpoint.h + ../../Services/WebContent/WebContentClientEndpoint.h + ../../Services/WebContent/WebContentServerEndpoint.h +) + +set_property(GLOBAL PROPERTY wrapper_sources) +function(add_wrapper_sources) + get_property(tmp GLOBAL PROPERTY wrapper_sources) + foreach(arg ${ARGV}) + set(tmp ${tmp} + ${arg} + ) + endforeach() + set_property(GLOBAL PROPERTY wrapper_sources "${tmp}") +endfunction(add_wrapper_sources) + +function(libweb_js_wrapper class) + get_filename_component(basename ${class} NAME) + add_wrapper_sources(Bindings/${basename}Wrapper.cpp Bindings/${basename}Wrapper.h) + add_custom_command( + OUTPUT Bindings/${basename}Wrapper.h + COMMAND ${write_if_different} Bindings/${basename}Wrapper.h CodeGenerators/WrapperGenerator --header ${CMAKE_CURRENT_SOURCE_DIR}/${class}.idl + VERBATIM + DEPENDS WrapperGenerator + MAIN_DEPENDENCY ${class}.idl + ) + add_custom_command( + OUTPUT Bindings/${basename}Wrapper.cpp + COMMAND ${write_if_different} Bindings/${basename}Wrapper.cpp CodeGenerators/WrapperGenerator --implementation ${CMAKE_CURRENT_SOURCE_DIR}/${class}.idl + VERBATIM + DEPENDS WrapperGenerator + MAIN_DEPENDENCY ${class}.idl + ) + add_custom_target(generate_${basename}Wrapper.h DEPENDS Bindings/${class}Wrapper.h) + add_custom_target(generate_${basename}Wrapper.cpp DEPENDS Bindings/${class}Wrapper.cpp) +endfunction() + +libweb_js_wrapper(DOM/CharacterData) +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) +libweb_js_wrapper(DOM/ShadowRoot) +libweb_js_wrapper(DOM/Node) +libweb_js_wrapper(DOM/Text) +libweb_js_wrapper(HTML/CanvasRenderingContext2D) +libweb_js_wrapper(HTML/HTMLAnchorElement) +libweb_js_wrapper(HTML/HTMLAreaElement) +libweb_js_wrapper(HTML/HTMLAudioElement) +libweb_js_wrapper(HTML/HTMLBaseElement) +libweb_js_wrapper(HTML/HTMLBodyElement) +libweb_js_wrapper(HTML/HTMLBRElement) +libweb_js_wrapper(HTML/HTMLButtonElement) +libweb_js_wrapper(HTML/HTMLCanvasElement) +libweb_js_wrapper(HTML/HTMLDataElement) +libweb_js_wrapper(HTML/HTMLDataListElement) +libweb_js_wrapper(HTML/HTMLDetailsElement) +libweb_js_wrapper(HTML/HTMLDialogElement) +libweb_js_wrapper(HTML/HTMLDirectoryElement) +libweb_js_wrapper(HTML/HTMLDivElement) +libweb_js_wrapper(HTML/HTMLDListElement) +libweb_js_wrapper(HTML/HTMLElement) +libweb_js_wrapper(HTML/HTMLEmbedElement) +libweb_js_wrapper(HTML/HTMLFieldSetElement) +libweb_js_wrapper(HTML/HTMLFontElement) +libweb_js_wrapper(HTML/HTMLFormElement) +libweb_js_wrapper(HTML/HTMLFrameElement) +libweb_js_wrapper(HTML/HTMLFrameSetElement) +libweb_js_wrapper(HTML/HTMLHeadElement) +libweb_js_wrapper(HTML/HTMLHeadingElement) +libweb_js_wrapper(HTML/HTMLHRElement) +libweb_js_wrapper(HTML/HTMLHtmlElement) +libweb_js_wrapper(HTML/HTMLIFrameElement) +libweb_js_wrapper(HTML/HTMLImageElement) +libweb_js_wrapper(HTML/HTMLInputElement) +libweb_js_wrapper(HTML/HTMLLabelElement) +libweb_js_wrapper(HTML/HTMLLegendElement) +libweb_js_wrapper(HTML/HTMLLIElement) +libweb_js_wrapper(HTML/HTMLLinkElement) +libweb_js_wrapper(HTML/HTMLMapElement) +libweb_js_wrapper(HTML/HTMLMarqueeElement) +libweb_js_wrapper(HTML/HTMLMediaElement) +libweb_js_wrapper(HTML/HTMLMenuElement) +libweb_js_wrapper(HTML/HTMLMetaElement) +libweb_js_wrapper(HTML/HTMLMeterElement) +libweb_js_wrapper(HTML/HTMLModElement) +libweb_js_wrapper(HTML/HTMLObjectElement) +libweb_js_wrapper(HTML/HTMLOListElement) +libweb_js_wrapper(HTML/HTMLOptGroupElement) +libweb_js_wrapper(HTML/HTMLOptionElement) +libweb_js_wrapper(HTML/HTMLOutputElement) +libweb_js_wrapper(HTML/HTMLParagraphElement) +libweb_js_wrapper(HTML/HTMLParamElement) +libweb_js_wrapper(HTML/HTMLPictureElement) +libweb_js_wrapper(HTML/HTMLPreElement) +libweb_js_wrapper(HTML/HTMLProgressElement) +libweb_js_wrapper(HTML/HTMLQuoteElement) +libweb_js_wrapper(HTML/HTMLScriptElement) +libweb_js_wrapper(HTML/HTMLSelectElement) +libweb_js_wrapper(HTML/HTMLSlotElement) +libweb_js_wrapper(HTML/HTMLSourceElement) +libweb_js_wrapper(HTML/HTMLSpanElement) +libweb_js_wrapper(HTML/HTMLStyleElement) +libweb_js_wrapper(HTML/HTMLTableCaptionElement) +libweb_js_wrapper(HTML/HTMLTableCellElement) +libweb_js_wrapper(HTML/HTMLTableColElement) +libweb_js_wrapper(HTML/HTMLTableElement) +libweb_js_wrapper(HTML/HTMLTableRowElement) +libweb_js_wrapper(HTML/HTMLTableSectionElement) +libweb_js_wrapper(HTML/HTMLTemplateElement) +libweb_js_wrapper(HTML/HTMLTextAreaElement) +libweb_js_wrapper(HTML/HTMLTimeElement) +libweb_js_wrapper(HTML/HTMLTitleElement) +libweb_js_wrapper(HTML/HTMLTrackElement) +libweb_js_wrapper(HTML/HTMLUListElement) +libweb_js_wrapper(HTML/HTMLUnknownElement) +libweb_js_wrapper(HTML/HTMLVideoElement) +libweb_js_wrapper(HTML/ImageData) +libweb_js_wrapper(HTML/SubmitEvent) +libweb_js_wrapper(HighResolutionTime/Performance) +libweb_js_wrapper(SVG/SVGElement) +libweb_js_wrapper(SVG/SVGGeometryElement) +libweb_js_wrapper(SVG/SVGGraphicsElement) +libweb_js_wrapper(SVG/SVGPathElement) +libweb_js_wrapper(SVG/SVGSVGElement) +libweb_js_wrapper(UIEvents/MouseEvent) +libweb_js_wrapper(UIEvents/UIEvent) + +get_property(WRAPPER_SOURCES GLOBAL PROPERTY wrapper_sources) +set(SOURCES ${SOURCES} ${WRAPPER_SOURCES}) + +add_custom_command( + OUTPUT CSS/PropertyID.h + COMMAND ${write_if_different} CSS/PropertyID.h CodeGenerators/Generate_CSS_PropertyID_h ${CMAKE_CURRENT_SOURCE_DIR}/CSS/Properties.json + VERBATIM + DEPENDS Generate_CSS_PropertyID_h + MAIN_DEPENDENCY CSS/Properties.json +) +add_custom_target(generate_PropertyID.h DEPENDS CSS/PropertyID.h) + +add_custom_command( + OUTPUT CSS/PropertyID.cpp + COMMAND /bin/mkdir -p CSS + COMMAND ${write_if_different} CSS/PropertyID.cpp CodeGenerators/Generate_CSS_PropertyID_cpp ${CMAKE_CURRENT_SOURCE_DIR}/CSS/Properties.json + VERBATIM + DEPENDS Generate_CSS_PropertyID_cpp + MAIN_DEPENDENCY CSS/Properties.json +) + +add_custom_command( + OUTPUT CSS/ValueID.h + COMMAND ${write_if_different} CSS/ValueID.h CodeGenerators/Generate_CSS_ValueID_h ${CMAKE_CURRENT_SOURCE_DIR}/CSS/Identifiers.json + VERBATIM + DEPENDS Generate_CSS_ValueID_h + MAIN_DEPENDENCY CSS/Identifiers.json +) +add_custom_target(generate_ValueID.h DEPENDS CSS/ValueID.h) + +add_custom_command( + OUTPUT CSS/ValueID.cpp + COMMAND /bin/mkdir -p CSS + COMMAND ${write_if_different} CSS/ValueID.cpp CodeGenerators/Generate_CSS_ValueID_cpp ${CMAKE_CURRENT_SOURCE_DIR}/CSS/Identifiers.json + VERBATIM + DEPENDS Generate_CSS_ValueID_cpp + MAIN_DEPENDENCY CSS/Identifiers.json +) + +add_custom_command( + OUTPUT CSS/DefaultStyleSheetSource.cpp + COMMAND ${write_if_different} CSS/DefaultStyleSheetSource.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Scripts/GenerateStyleSheetSource.sh default_stylesheet_source ${CMAKE_CURRENT_SOURCE_DIR}/CSS/Default.css + VERBATIM + DEPENDS Scripts/GenerateStyleSheetSource.sh + MAIN_DEPENDENCY CSS/Default.css +) + +add_custom_command( + OUTPUT CSS/QuirksModeStyleSheetSource.cpp + COMMAND ${write_if_different} CSS/QuirksModeStyleSheetSource.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Scripts/GenerateStyleSheetSource.sh quirks_mode_stylesheet_source ${CMAKE_CURRENT_SOURCE_DIR}/CSS/QuirksMode.css + VERBATIM + DEPENDS Scripts/GenerateStyleSheetSource.sh + MAIN_DEPENDENCY CSS/Default.css +) + +serenity_lib(LibWeb web) +target_link_libraries(LibWeb LibCore LibJS LibMarkdown LibGemini LibGUI LibGfx LibTextCodec LibProtocol LibImageDecoderClient) + +add_subdirectory(DumpLayoutTree) diff --git a/Userland/Libraries/LibWeb/CSS/.gitignore b/Userland/Libraries/LibWeb/CSS/.gitignore new file mode 100644 index 0000000000..ae00c71e95 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/.gitignore @@ -0,0 +1,3 @@ +DefaultStyleSheetSource.cpp +PropertyID.cpp +PropertyID.h diff --git a/Userland/Libraries/LibWeb/CSS/ComputedValues.h b/Userland/Libraries/LibWeb/CSS/ComputedValues.h new file mode 100644 index 0000000000..57866b07f4 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/ComputedValues.h @@ -0,0 +1,161 @@ +/* + * 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 <AK/Optional.h> +#include <LibWeb/CSS/LengthBox.h> +#include <LibWeb/CSS/StyleValue.h> + +namespace Web::CSS { + +class InitialValues { +public: + static CSS::Float float_() { return CSS::Float::None; } + static CSS::Clear clear() { return CSS::Clear::None; } + static CSS::WhiteSpace white_space() { return CSS::WhiteSpace::Normal; } + static CSS::TextAlign text_align() { return CSS::TextAlign::Left; } + static CSS::Position position() { return CSS::Position::Static; } + static CSS::TextDecorationLine text_decoration_line() { return CSS::TextDecorationLine::None; } + static CSS::TextTransform text_transform() { return CSS::TextTransform::None; } + static CSS::Display display() { return CSS::Display::Inline; } + static Color color() { return Color::Black; } + static Color background_color() { return Color::Transparent; } + static CSS::ListStyleType list_style_type() { return CSS::ListStyleType::Disc; } +}; + +struct BorderData { +public: + Color color { Color::Transparent }; + CSS::LineStyle line_style { CSS::LineStyle::None }; + float width { 0 }; +}; + +class ComputedValues { +public: + CSS::Float float_() const { return m_noninherited.float_; } + CSS::Clear clear() const { return m_noninherited.clear; } + CSS::Display display() const { return m_noninherited.display; } + Optional<int> z_index() const { return m_noninherited.z_index; } + CSS::TextAlign text_align() const { return m_inherited.text_align; } + CSS::TextDecorationLine text_decoration_line() const { return m_noninherited.text_decoration_line; } + CSS::TextTransform text_transform() const { return m_inherited.text_transform; } + CSS::Position position() const { return m_noninherited.position; } + CSS::WhiteSpace white_space() const { return m_inherited.white_space; } + const CSS::Length& width() const { return m_noninherited.width; } + const CSS::Length& min_width() const { return m_noninherited.min_width; } + const CSS::Length& max_width() const { return m_noninherited.max_width; } + const CSS::Length& height() const { return m_noninherited.height; } + const CSS::Length& min_height() const { return m_noninherited.min_height; } + const CSS::Length& max_height() const { return m_noninherited.max_height; } + + const CSS::LengthBox& offset() const { return m_noninherited.offset; } + const CSS::LengthBox& margin() const { return m_noninherited.margin; } + const CSS::LengthBox& padding() const { return m_noninherited.padding; } + + const BorderData& border_left() const { return m_noninherited.border_left; } + const BorderData& border_top() const { return m_noninherited.border_top; } + const BorderData& border_right() const { return m_noninherited.border_right; } + const BorderData& border_bottom() const { return m_noninherited.border_bottom; } + + Color color() const { return m_inherited.color; } + Color background_color() const { return m_noninherited.background_color; } + + CSS::ListStyleType list_style_type() const { return m_inherited.list_style_type; } + + ComputedValues clone_inherited_values() const + { + ComputedValues clone; + clone.m_inherited = m_inherited; + return clone; + } + +protected: + struct { + Color color { InitialValues::color() }; + CSS::TextAlign text_align { InitialValues::text_align() }; + CSS::TextTransform text_transform { InitialValues::text_transform() }; + CSS::WhiteSpace white_space { InitialValues::white_space() }; + CSS::ListStyleType list_style_type { InitialValues::list_style_type() }; + } m_inherited; + + struct { + CSS::Float float_ { InitialValues::float_() }; + CSS::Clear clear { InitialValues::clear() }; + CSS::Display display { InitialValues::display() }; + Optional<int> z_index; + CSS::TextDecorationLine text_decoration_line { InitialValues::text_decoration_line() }; + CSS::Position position { InitialValues::position() }; + CSS::Length width; + CSS::Length min_width; + CSS::Length max_width; + CSS::Length height; + CSS::Length min_height; + CSS::Length max_height; + CSS::LengthBox offset; + CSS::LengthBox margin; + CSS::LengthBox padding; + BorderData border_left; + BorderData border_top; + BorderData border_right; + BorderData border_bottom; + Color background_color { InitialValues::background_color() }; + } m_noninherited; +}; + +class ImmutableComputedValues final : public ComputedValues { +}; + +class MutableComputedValues final : public ComputedValues { +public: + void set_color(const Color& color) { m_inherited.color = color; } + void set_background_color(const Color& color) { m_noninherited.background_color = color; } + void set_float(CSS::Float value) { m_noninherited.float_ = value; } + void set_clear(CSS::Clear value) { m_noninherited.clear = value; } + void set_z_index(Optional<int> value) { m_noninherited.z_index = value; } + void set_text_align(CSS::TextAlign text_align) { m_inherited.text_align = text_align; } + void set_text_decoration_line(CSS::TextDecorationLine value) { m_noninherited.text_decoration_line = value; } + void set_text_transform(CSS::TextTransform value) { m_inherited.text_transform = value; } + void set_position(CSS::Position position) { m_noninherited.position = position; } + void set_white_space(CSS::WhiteSpace value) { m_inherited.white_space = value; } + void set_width(const CSS::Length& width) { m_noninherited.width = width; } + void set_min_width(const CSS::Length& width) { m_noninherited.min_width = width; } + void set_max_width(const CSS::Length& width) { m_noninherited.max_width = width; } + void set_height(const CSS::Length& height) { m_noninherited.height = height; } + void set_min_height(const CSS::Length& height) { m_noninherited.min_height = height; } + void set_max_height(const CSS::Length& height) { m_noninherited.max_height = height; } + void set_offset(const CSS::LengthBox& offset) { m_noninherited.offset = offset; } + void set_margin(const CSS::LengthBox& margin) { m_noninherited.margin = margin; } + void set_padding(const CSS::LengthBox& padding) { m_noninherited.padding = padding; } + void set_list_style_type(CSS::ListStyleType value) { m_inherited.list_style_type = value; } + void set_display(CSS::Display value) { m_noninherited.display = value; } + BorderData& border_left() { return m_noninherited.border_left; } + BorderData& border_top() { return m_noninherited.border_top; } + BorderData& border_right() { return m_noninherited.border_right; } + BorderData& border_bottom() { return m_noninherited.border_bottom; } +}; + +} diff --git a/Userland/Libraries/LibWeb/CSS/Default.css b/Userland/Libraries/LibWeb/CSS/Default.css new file mode 100644 index 0000000000..a63c7873ef --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Default.css @@ -0,0 +1,196 @@ +html { + font-family: sans-serif; +} + +head, +link, +meta, +script, +style, +title { + display: none; +} + +body { + margin: 8px; +} + +h1, +h2 { + font-family: Pebbleton; + font-size: 14px; + font-weight: bold; +} + +h3, +h4, +h5, +h6 { + font-weight: bold; +} + +pre { + font-family: monospace; + margin-bottom: 8px; + margin-top: 8px; + white-space: pre; +} + +code { + font-family: monospace; +} + +u, +ins { + text-decoration: underline; +} + +strong, +b { + font-weight: bold; +} + +html, +address, +blockquote, +body, +dd, +div, +dl, +dt, +fieldset, +form, +frame, +frameset, +hgroup, +h1, +h2, +h3, +h4, +h5, +h6, +noframes, +ol, +p, +ul, +center, +dir, +hr, +menu, +pre, +header, +footer, +nav, +main, +article, +aside, +section { + display: block; +} + +center { + text-align: -libweb-center; +} + +h1, +h2, +h3 { + margin: 8px 0 8px 0; +} + +h4, +p, +blockquote, +ul, +fieldset, +form, +ol, +dl, +dir, +menu { + margin: 4px 0 4px 0; +} + +h5, +h6 { + margin: 2px 0 2px 0; +} + +li { + display: list-item; + margin-left: 8px; + margin-top: 2px; + margin-bottom: 2px; +} + +a:link { + color: -libweb-link; + text-decoration: underline; +} + +a:hover { + color: red; +} + +hr { + margin-top: 0.5em; + margin-bottom: 0.5em; + border: 1px inset #888888; +} + +blink { + display: inline; +} + +table { + display: table; +} + +thead { + display: table-header-group; + vertical-align: middle; + border-color: inherit; +} + +tbody { + display: table-row-group; + vertical-align: middle; + border-color: inherit; +} + +tfoot { + display: table-footer-group; + vertical-align: middle; + border-color: inherit; +} + +tr { + display: table-row; +} + +td, +th { + display: table-cell; +} + +col { + display: table-column; +} + +colgroup { + display: table-column-group; +} + +basefont { + display: block; +} + +blockquote { + margin-left: 25px; + margin-right: 25px; +} + +ul, +ol { + padding-left: 20px; +} diff --git a/Userland/Libraries/LibWeb/CSS/Identifiers.json b/Userland/Libraries/LibWeb/CSS/Identifiers.json new file mode 100644 index 0000000000..5e412147fe --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Identifiers.json @@ -0,0 +1,122 @@ +[ + "-libweb-center", + "-libweb-link", + "-libweb-palette-active-link", + "-libweb-palette-active-window-border1", + "-libweb-palette-active-window-border2", + "-libweb-palette-active-window-title", + "-libweb-palette-base", + "-libweb-palette-base-text", + "-libweb-palette-button", + "-libweb-palette-button-text", + "-libweb-palette-desktop-background", + "-libweb-palette-focus-outline", + "-libweb-palette-highlight-window-border1", + "-libweb-palette-highlight-window-border2", + "-libweb-palette-highlight-window-title", + "-libweb-palette-hover-highlight", + "-libweb-palette-inactive-selection", + "-libweb-palette-inactive-selection-text", + "-libweb-palette-inactive-window-border1", + "-libweb-palette-inactive-window-border2", + "-libweb-palette-inactive-window-title", + "-libweb-palette-link", + "-libweb-palette-menu-base", + "-libweb-palette-menu-base-text", + "-libweb-palette-menu-selection", + "-libweb-palette-menu-selection-text", + "-libweb-palette-menu-stripe", + "-libweb-palette-moving-window-border1", + "-libweb-palette-moving-window-border2", + "-libweb-palette-moving-window-title", + "-libweb-palette-rubber-band-border", + "-libweb-palette-rubber-band-fill", + "-libweb-palette-ruler", + "-libweb-palette-ruler-active-text", + "-libweb-palette-ruler-border", + "-libweb-palette-ruler-inactive-text", + "-libweb-palette-selection", + "-libweb-palette-selection-text", + "-libweb-palette-syntax-comment", + "-libweb-palette-syntax-control-keyword", + "-libweb-palette-syntax-identifier", + "-libweb-palette-syntax-keyword", + "-libweb-palette-syntax-number", + "-libweb-palette-syntax-operator", + "-libweb-palette-syntax-preprocessor-statement", + "-libweb-palette-syntax-preprocessor-value", + "-libweb-palette-syntax-punctuation", + "-libweb-palette-syntax-string", + "-libweb-palette-syntax-type", + "-libweb-palette-text-cursor", + "-libweb-palette-threed-highlight", + "-libweb-palette-threed-shadow1", + "-libweb-palette-threed-shadow2", + "-libweb-palette-visited-link", + "-libweb-palette-window", + "-libweb-palette-window-text", + "absolute", + "blink", + "block", + "bold", + "bolder", + "both", + "capitalize", + "center", + "circle", + "dashed", + "decimal", + "disc", + "dotted", + "double", + "fixed", + "full-size-kana", + "full-width", + "groove", + "hidden", + "inline", + "inline-block", + "inset", + "justify", + "large", + "larger", + "left", + "lighter", + "line-through", + "list-item", + "lowercase", + "medium", + "none", + "normal", + "nowrap", + "outset", + "overline", + "pre", + "pre-line", + "pre-wrap", + "relative", + "ridge", + "right", + "small", + "smaller", + "solid", + "square", + "static", + "sticky", + "table", + "table-caption", + "table-cell", + "table-column", + "table-column-group", + "table-footer-group", + "table-header-group", + "table-row", + "table-row-group", + "underline", + "uppercase", + "x-large", + "x-small", + "xx-large", + "xx-small", + "xxx-large" +] diff --git a/Userland/Libraries/LibWeb/CSS/Length.cpp b/Userland/Libraries/LibWeb/CSS/Length.cpp new file mode 100644 index 0000000000..08b2ca500f --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Length.cpp @@ -0,0 +1,103 @@ +/* + * 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 <LibWeb/CSS/Length.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/HTML/HTMLHtmlElement.h> +#include <LibWeb/Page/Frame.h> + +namespace Web::CSS { + +float Length::relative_length_to_px(const Layout::Node& layout_node) const +{ + switch (m_type) { + case Type::Ex: + return m_value * layout_node.font().x_height(); + case Type::Em: + return m_value * layout_node.font_size(); + case Type::Rem: + return m_value * layout_node.document().document_element()->layout_node()->font_size(); + case Type::Vw: + return layout_node.document().frame()->viewport_rect().width() * (m_value / 100); + case Type::Vh: + return layout_node.document().frame()->viewport_rect().height() * (m_value / 100); + case Type::Vmin: { + auto viewport = layout_node.document().frame()->viewport_rect(); + + return min(viewport.width(), viewport.height()) * (m_value / 100); + } + case Type::Vmax: { + auto viewport = layout_node.document().frame()->viewport_rect(); + + return max(viewport.width(), viewport.height()) * (m_value / 100); + } + default: + ASSERT_NOT_REACHED(); + } +} + +const char* Length::unit_name() const +{ + switch (m_type) { + case Type::Cm: + return "cm"; + case Type::In: + return "in"; + case Type::Px: + return "px"; + case Type::Pt: + return "pt"; + case Type::Mm: + return "mm"; + case Type::Q: + return "Q"; + case Type::Pc: + return "pc"; + case Type::Ex: + return "ex"; + case Type::Em: + return "em"; + case Type::Rem: + return "rem"; + case Type::Auto: + return "auto"; + case Type::Percentage: + return "%"; + case Type::Undefined: + return "undefined"; + case Type::Vh: + return "vh"; + case Type::Vw: + return "vw"; + case Type::Vmax: + return "vmax"; + case Type::Vmin: + return "vmin"; + } + ASSERT_NOT_REACHED(); +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/Length.h b/Userland/Libraries/LibWeb/CSS/Length.h new file mode 100644 index 0000000000..bce76a059c --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Length.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2018-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 <AK/String.h> +#include <LibWeb/Forward.h> + +namespace Web::CSS { + +class Length { +public: + enum class Type { + Undefined, + Percentage, + Auto, + Cm, + In, + Mm, + Q, + Px, + Pt, + Pc, + Ex, + Em, + Rem, + Vh, + Vw, + Vmax, + Vmin, + }; + + Length() { } + Length(int value, Type type) + : m_type(type) + , m_value(value) + { + } + Length(float value, Type type) + : m_type(type) + , m_value(value) + { + } + + static Length make_auto() { return Length(0, Type::Auto); } + static Length make_px(float value) { return Length(value, Type::Px); } + + Length resolved(const Length& fallback_for_undefined, const Layout::Node& layout_node, float reference_for_percent) const + { + if (is_undefined()) + return fallback_for_undefined; + if (is_percentage()) + return make_px(raw_value() / 100.0 * reference_for_percent); + if (is_relative()) + return make_px(to_px(layout_node)); + return *this; + } + + Length resolved_or_auto(const Layout::Node& layout_node, float reference_for_percent) const + { + return resolved(make_auto(), layout_node, reference_for_percent); + } + + Length resolved_or_zero(const Layout::Node& layout_node, float reference_for_percent) const + { + return resolved(make_px(0), layout_node, reference_for_percent); + } + + bool is_undefined_or_auto() const { return m_type == Type::Undefined || m_type == Type::Auto; } + bool is_undefined() const { return m_type == Type::Undefined; } + bool is_percentage() const { return m_type == Type::Percentage; } + bool is_auto() const { return m_type == Type::Auto; } + + bool is_absolute() const + { + return m_type == Type::Cm + || m_type == Type::In + || m_type == Type::Mm + || m_type == Type::Px + || m_type == Type::Pt + || m_type == Type::Pc + || m_type == Type::Q; + } + + bool is_relative() const + { + return m_type == Type::Ex + || m_type == Type::Em + || m_type == Type::Rem + || m_type == Type::Vh + || m_type == Type::Vw + || m_type == Type::Vmax + || m_type == Type::Vmin; + } + + float raw_value() const { return m_value; } + ALWAYS_INLINE float to_px(const Layout::Node& layout_node) const + { + if (is_relative()) + return relative_length_to_px(layout_node); + constexpr float inch_pixels = 96.0f; + constexpr float centimeter_pixels = (inch_pixels / 2.54f); + switch (m_type) { + case Type::Auto: + return 0; + case Type::Cm: + return m_value * centimeter_pixels; // 1cm = 96px/2.54 + case Type::In: + return m_value * inch_pixels; // 1in = 2.54 cm = 96px + case Type::Px: + return m_value; // 1px = 1/96th of 1in + case Type::Pt: + return m_value * ((1.0f / 72.0f) * inch_pixels); // 1pt = 1/72th of 1in + case Type::Pc: + return m_value * ((1.0f / 6.0f) * inch_pixels); // 1pc = 1/6th of 1in + case Type::Mm: + return m_value * ((1.0f / 10.0f) * centimeter_pixels); // 1mm = 1/10th of 1cm + case Type::Q: + return m_value * ((1.0f / 40.0f) * centimeter_pixels); // 1Q = 1/40th of 1cm + case Type::Undefined: + case Type::Percentage: + default: + ASSERT_NOT_REACHED(); + } + } + + String to_string() const + { + if (is_auto()) + return "[auto]"; + return String::formatted("[{} {}]", m_value, unit_name()); + } + + bool operator==(const Length& other) const + { + return m_type == other.m_type && m_value == other.m_value; + } + + bool operator!=(const Length& other) const + { + return !(*this == other); + } + +private: + float relative_length_to_px(const Layout::Node&) const; + + const char* unit_name() const; + + Type m_type { Type::Undefined }; + float m_value { 0 }; +}; + +inline const LogStream& operator<<(const LogStream& stream, const Length& value) +{ + return stream << value.to_string(); +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/LengthBox.h b/Userland/Libraries/LibWeb/CSS/LengthBox.h new file mode 100644 index 0000000000..931d6c6da2 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/LengthBox.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-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/CSS/Length.h> + +namespace Web::CSS { + +struct LengthBox { + Length top { Length::make_auto() }; + Length right { Length::make_auto() }; + Length bottom { Length::make_auto() }; + Length left { Length::make_auto() }; +}; + +} diff --git a/Userland/Libraries/LibWeb/CSS/Parser/CSSParser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/CSSParser.cpp new file mode 100644 index 0000000000..62bee3b733 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Parser/CSSParser.cpp @@ -0,0 +1,902 @@ +/* + * Copyright (c) 2018-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/HashMap.h> +#include <LibWeb/CSS/Parser/CSSParser.h> +#include <LibWeb/CSS/PropertyID.h> +#include <LibWeb/CSS/StyleSheet.h> +#include <LibWeb/DOM/Document.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define PARSE_ASSERT(x) \ + if (!(x)) { \ + dbg() << "CSS PARSER ASSERTION FAILED: " << #x; \ + dbg() << "At character# " << index << " in CSS: _" << css << "_"; \ + ASSERT_NOT_REACHED(); \ + } + +#define PARSE_ERROR() \ + do { \ + dbgln("CSS parse error"); \ + } while (0) + +namespace Web { + +namespace CSS { + +ParsingContext::ParsingContext() +{ +} + +ParsingContext::ParsingContext(const DOM::Document& document) + : m_document(&document) +{ +} + +ParsingContext::ParsingContext(const DOM::ParentNode& parent_node) + : m_document(&parent_node.document()) +{ +} + +bool ParsingContext::in_quirks_mode() const +{ + return m_document ? m_document->in_quirks_mode() : false; +} + +} + +static Optional<Color> parse_css_color(const CSS::ParsingContext&, const StringView& view) +{ + if (view.equals_ignoring_case("transparent")) + return Color::from_rgba(0x00000000); + + auto color = Color::from_string(view.to_string().to_lowercase()); + if (color.has_value()) + return color; + + return {}; +} + +static Optional<float> try_parse_float(const StringView& string) +{ + const char* str = string.characters_without_null_termination(); + size_t len = string.length(); + size_t weight = 1; + int exp_val = 0; + float value = 0.0f; + float fraction = 0.0f; + bool has_sign = false; + bool is_negative = false; + bool is_fractional = false; + bool is_scientific = false; + + if (str[0] == '-') { + is_negative = true; + has_sign = true; + } + if (str[0] == '+') { + has_sign = true; + } + + for (size_t i = has_sign; i < len; i++) { + + // Looks like we're about to start working on the fractional part + if (str[i] == '.') { + is_fractional = true; + continue; + } + + if (str[i] == 'e' || str[i] == 'E') { + if (str[i + 1] == '-' || str[i + 1] == '+') + exp_val = atoi(str + i + 2); + else + exp_val = atoi(str + i + 1); + + is_scientific = true; + continue; + } + + if (str[i] < '0' || str[i] > '9' || exp_val != 0) { + return {}; + continue; + } + + if (is_fractional) { + fraction *= 10; + fraction += str[i] - '0'; + weight *= 10; + } else { + value = value * 10; + value += str[i] - '0'; + } + } + + fraction /= weight; + value += fraction; + + if (is_scientific) { + bool divide = exp_val < 0; + if (divide) + exp_val *= -1; + + for (int i = 0; i < exp_val; i++) { + if (divide) + value /= 10; + else + value *= 10; + } + } + + return is_negative ? -value : value; +} + +static CSS::Length parse_length(const CSS::ParsingContext& context, const StringView& view, bool& is_bad_length) +{ + CSS::Length::Type type = CSS::Length::Type::Undefined; + Optional<float> value; + + if (view.ends_with('%')) { + type = CSS::Length::Type::Percentage; + value = try_parse_float(view.substring_view(0, view.length() - 1)); + } else if (view.ends_with("px", CaseSensitivity::CaseInsensitive)) { + type = CSS::Length::Type::Px; + value = try_parse_float(view.substring_view(0, view.length() - 2)); + } else if (view.ends_with("pt", CaseSensitivity::CaseInsensitive)) { + type = CSS::Length::Type::Pt; + value = try_parse_float(view.substring_view(0, view.length() - 2)); + } else if (view.ends_with("pc", CaseSensitivity::CaseInsensitive)) { + type = CSS::Length::Type::Pc; + value = try_parse_float(view.substring_view(0, view.length() - 2)); + } else if (view.ends_with("mm", CaseSensitivity::CaseInsensitive)) { + type = CSS::Length::Type::Mm; + value = try_parse_float(view.substring_view(0, view.length() - 2)); + } else if (view.ends_with("rem", CaseSensitivity::CaseInsensitive)) { + type = CSS::Length::Type::Rem; + value = try_parse_float(view.substring_view(0, view.length() - 3)); + } else if (view.ends_with("em", CaseSensitivity::CaseInsensitive)) { + type = CSS::Length::Type::Em; + value = try_parse_float(view.substring_view(0, view.length() - 2)); + } else if (view.ends_with("ex", CaseSensitivity::CaseInsensitive)) { + type = CSS::Length::Type::Ex; + value = try_parse_float(view.substring_view(0, view.length() - 2)); + } else if (view.ends_with("vw", CaseSensitivity::CaseInsensitive)) { + type = CSS::Length::Type::Vw; + value = try_parse_float(view.substring_view(0, view.length() - 2)); + } else if (view.ends_with("vh", CaseSensitivity::CaseInsensitive)) { + type = CSS::Length::Type::Vh; + value = try_parse_float(view.substring_view(0, view.length() - 2)); + } else if (view.ends_with("vmax", CaseSensitivity::CaseInsensitive)) { + type = CSS::Length::Type::Vmax; + value = try_parse_float(view.substring_view(0, view.length() - 4)); + } else if (view.ends_with("vmin", CaseSensitivity::CaseInsensitive)) { + type = CSS::Length::Type::Vmin; + value = try_parse_float(view.substring_view(0, view.length() - 4)); + } else if (view.ends_with("cm", CaseSensitivity::CaseInsensitive)) { + type = CSS::Length::Type::Cm; + value = try_parse_float(view.substring_view(0, view.length() - 2)); + } else if (view.ends_with("in", CaseSensitivity::CaseInsensitive)) { + type = CSS::Length::Type::In; + value = try_parse_float(view.substring_view(0, view.length() - 2)); + } else if (view.ends_with("Q", CaseSensitivity::CaseInsensitive)) { + type = CSS::Length::Type::Q; + value = try_parse_float(view.substring_view(0, view.length() - 1)); + } else if (view == "0") { + type = CSS::Length::Type::Px; + value = 0; + } else if (context.in_quirks_mode()) { + type = CSS::Length::Type::Px; + value = try_parse_float(view); + } else { + value = try_parse_float(view); + if (value.has_value()) + is_bad_length = true; + } + + if (!value.has_value()) + return {}; + + return CSS::Length(value.value(), type); +} + +static bool takes_integer_value(CSS::PropertyID property_id) +{ + return property_id == CSS::PropertyID::ZIndex || property_id == CSS::PropertyID::FontWeight; +} + +RefPtr<CSS::StyleValue> parse_css_value(const CSS::ParsingContext& context, const StringView& string, CSS::PropertyID property_id) +{ + bool is_bad_length = false; + + if (takes_integer_value(property_id)) { + auto integer = string.to_int(); + if (integer.has_value()) + return CSS::LengthStyleValue::create(CSS::Length::make_px(integer.value())); + } + + auto length = parse_length(context, string, is_bad_length); + if (is_bad_length) + return nullptr; + if (!length.is_undefined()) + return CSS::LengthStyleValue::create(length); + + if (string.equals_ignoring_case("inherit")) + return CSS::InheritStyleValue::create(); + if (string.equals_ignoring_case("initial")) + return CSS::InitialStyleValue::create(); + if (string.equals_ignoring_case("auto")) + return CSS::LengthStyleValue::create(CSS::Length::make_auto()); + + auto value_id = CSS::value_id_from_string(string); + if (value_id != CSS::ValueID::Invalid) + return CSS::IdentifierStyleValue::create(value_id); + + auto color = parse_css_color(context, string); + if (color.has_value()) + return CSS::ColorStyleValue::create(color.value()); + + return CSS::StringStyleValue::create(string); +} + +RefPtr<CSS::LengthStyleValue> parse_line_width(const CSS::ParsingContext& context, const StringView& part) +{ + auto value = parse_css_value(context, part); + if (value && value->is_length()) + return static_ptr_cast<CSS::LengthStyleValue>(value); + return nullptr; +} + +RefPtr<CSS::ColorStyleValue> parse_color(const CSS::ParsingContext& context, const StringView& part) +{ + auto value = parse_css_value(context, part); + if (value && value->is_color()) + return static_ptr_cast<CSS::ColorStyleValue>(value); + return nullptr; +} + +RefPtr<CSS::StringStyleValue> parse_line_style(const CSS::ParsingContext& context, const StringView& part) +{ + auto parsed_value = parse_css_value(context, part); + if (!parsed_value || !parsed_value->is_string()) + return nullptr; + auto value = static_ptr_cast<CSS::StringStyleValue>(parsed_value); + if (value->to_string() == "dotted") + return value; + if (value->to_string() == "dashed") + return value; + if (value->to_string() == "solid") + return value; + if (value->to_string() == "double") + return value; + if (value->to_string() == "groove") + return value; + if (value->to_string() == "ridge") + return value; + return nullptr; +} + +class CSSParser { +public: + CSSParser(const CSS::ParsingContext& context, const StringView& input) + : m_context(context) + , css(input) + { + } + + bool next_is(const char* str) const + { + size_t len = strlen(str); + for (size_t i = 0; i < len; ++i) { + if (peek(i) != str[i]) + return false; + } + return true; + } + + char peek(size_t offset = 0) const + { + if ((index + offset) < css.length()) + return css[index + offset]; + return 0; + } + + bool consume_specific(char ch) + { + if (peek() != ch) { + dbgln("CSSParser: Peeked '{:c}' wanted specific '{:c}'", peek(), ch); + } + if (!peek()) { + PARSE_ERROR(); + return false; + } + if (peek() != ch) { + PARSE_ERROR(); + ++index; + return false; + } + ++index; + return true; + } + + char consume_one() + { + PARSE_ASSERT(index < css.length()); + return css[index++]; + }; + + bool consume_whitespace_or_comments() + { + size_t original_index = index; + bool in_comment = false; + for (; index < css.length(); ++index) { + char ch = peek(); + if (isspace(ch)) + continue; + if (!in_comment && ch == '/' && peek(1) == '*') { + in_comment = true; + ++index; + continue; + } + if (in_comment && ch == '*' && peek(1) == '/') { + in_comment = false; + ++index; + continue; + } + if (in_comment) + continue; + break; + } + return original_index != index; + } + + bool is_valid_selector_char(char ch) const + { + return isalnum(ch) || ch == '-' || ch == '_' || ch == '(' || ch == ')' || ch == '@'; + } + + bool is_combinator(char ch) const + { + return ch == '~' || ch == '>' || ch == '+'; + } + + Optional<CSS::Selector::SimpleSelector> parse_simple_selector() + { + auto index_at_start = index; + + if (consume_whitespace_or_comments()) + return {}; + + if (!peek() || peek() == '{' || peek() == ',' || is_combinator(peek())) + return {}; + + CSS::Selector::SimpleSelector::Type type; + + if (peek() == '*') { + type = CSS::Selector::SimpleSelector::Type::Universal; + consume_one(); + return CSS::Selector::SimpleSelector { + type, + CSS::Selector::SimpleSelector::PseudoClass::None, + CSS::Selector::SimpleSelector::PseudoElement::None, + String(), + CSS::Selector::SimpleSelector::AttributeMatchType::None, + String(), + String() + }; + } + + if (peek() == '.') { + type = CSS::Selector::SimpleSelector::Type::Class; + consume_one(); + } else if (peek() == '#') { + type = CSS::Selector::SimpleSelector::Type::Id; + consume_one(); + } else if (isalpha(peek())) { + type = CSS::Selector::SimpleSelector::Type::TagName; + } else { + type = CSS::Selector::SimpleSelector::Type::Universal; + } + + if (type != CSS::Selector::SimpleSelector::Type::Universal) { + while (is_valid_selector_char(peek())) + buffer.append(consume_one()); + PARSE_ASSERT(!buffer.is_null()); + } + + auto value = String::copy(buffer); + + if (type == CSS::Selector::SimpleSelector::Type::TagName) { + // Some stylesheets use uppercase tag names, so here's a hack to just lowercase them internally. + value = value.to_lowercase(); + } + + CSS::Selector::SimpleSelector simple_selector { + type, + CSS::Selector::SimpleSelector::PseudoClass::None, + CSS::Selector::SimpleSelector::PseudoElement::None, + value, + CSS::Selector::SimpleSelector::AttributeMatchType::None, + String(), + String() + }; + buffer.clear(); + + if (peek() == '[') { + CSS::Selector::SimpleSelector::AttributeMatchType attribute_match_type = CSS::Selector::SimpleSelector::AttributeMatchType::HasAttribute; + String attribute_name; + String attribute_value; + bool in_value = false; + consume_specific('['); + char expected_end_of_attribute_selector = ']'; + while (peek() != expected_end_of_attribute_selector) { + char ch = consume_one(); + if (ch == '=' || (ch == '~' && peek() == '=')) { + if (ch == '=') { + attribute_match_type = CSS::Selector::SimpleSelector::AttributeMatchType::ExactValueMatch; + } else if (ch == '~') { + consume_one(); + attribute_match_type = CSS::Selector::SimpleSelector::AttributeMatchType::Contains; + } + attribute_name = String::copy(buffer); + buffer.clear(); + in_value = true; + consume_whitespace_or_comments(); + if (peek() == '\'') { + expected_end_of_attribute_selector = '\''; + consume_one(); + } else if (peek() == '"') { + expected_end_of_attribute_selector = '"'; + consume_one(); + } + continue; + } + // FIXME: This is a hack that will go away when we replace this with a big boy CSS parser. + if (ch == '\\') + ch = consume_one(); + buffer.append(ch); + } + if (in_value) + attribute_value = String::copy(buffer); + else + attribute_name = String::copy(buffer); + buffer.clear(); + simple_selector.attribute_match_type = attribute_match_type; + simple_selector.attribute_name = attribute_name; + simple_selector.attribute_value = attribute_value; + if (expected_end_of_attribute_selector != ']') { + if (!consume_specific(expected_end_of_attribute_selector)) + return {}; + } + consume_whitespace_or_comments(); + if (!consume_specific(']')) + return {}; + } + + if (peek() == ':') { + // FIXME: Implement pseudo elements. + [[maybe_unused]] bool is_pseudo_element = false; + consume_one(); + if (peek() == ':') { + is_pseudo_element = true; + consume_one(); + } + if (next_is("not")) { + buffer.append(consume_one()); + buffer.append(consume_one()); + buffer.append(consume_one()); + if (!consume_specific('(')) + return {}; + buffer.append('('); + while (peek() != ')') + buffer.append(consume_one()); + if (!consume_specific(')')) + return {}; + buffer.append(')'); + } else { + while (is_valid_selector_char(peek())) + buffer.append(consume_one()); + } + + auto pseudo_name = String::copy(buffer); + buffer.clear(); + + // Ignore for now, otherwise we produce a "false positive" selector + // and apply styles to the element itself, not its pseudo element + if (is_pseudo_element) + return {}; + + if (pseudo_name.equals_ignoring_case("link")) + simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Link; + else if (pseudo_name.equals_ignoring_case("visited")) + simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Visited; + else if (pseudo_name.equals_ignoring_case("hover")) + simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Hover; + else if (pseudo_name.equals_ignoring_case("focus")) + simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Focus; + else if (pseudo_name.equals_ignoring_case("first-child")) + simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::FirstChild; + else if (pseudo_name.equals_ignoring_case("last-child")) + simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::LastChild; + else if (pseudo_name.equals_ignoring_case("only-child")) + simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::OnlyChild; + else if (pseudo_name.equals_ignoring_case("empty")) + simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Empty; + else if (pseudo_name.equals_ignoring_case("root")) + simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Root; + else if (pseudo_name.equals_ignoring_case("before")) + simple_selector.pseudo_element = CSS::Selector::SimpleSelector::PseudoElement::Before; + else if (pseudo_name.equals_ignoring_case("after")) + simple_selector.pseudo_element = CSS::Selector::SimpleSelector::PseudoElement::After; + } + + if (index == index_at_start) { + // We consumed nothing. + return {}; + } + + return simple_selector; + } + + Optional<CSS::Selector::ComplexSelector> parse_complex_selector() + { + auto relation = CSS::Selector::ComplexSelector::Relation::Descendant; + + if (peek() == '{' || peek() == ',') + return {}; + + if (is_combinator(peek())) { + switch (peek()) { + case '>': + relation = CSS::Selector::ComplexSelector::Relation::ImmediateChild; + break; + case '+': + relation = CSS::Selector::ComplexSelector::Relation::AdjacentSibling; + break; + case '~': + relation = CSS::Selector::ComplexSelector::Relation::GeneralSibling; + break; + } + consume_one(); + consume_whitespace_or_comments(); + } + + consume_whitespace_or_comments(); + + Vector<CSS::Selector::SimpleSelector> simple_selectors; + for (;;) { + auto component = parse_simple_selector(); + if (!component.has_value()) + break; + simple_selectors.append(component.value()); + // If this assert triggers, we're most likely up to no good. + PARSE_ASSERT(simple_selectors.size() < 100); + } + + if (simple_selectors.is_empty()) + return {}; + + return CSS::Selector::ComplexSelector { relation, move(simple_selectors) }; + } + + void parse_selector() + { + Vector<CSS::Selector::ComplexSelector> complex_selectors; + + for (;;) { + auto index_before = index; + auto complex_selector = parse_complex_selector(); + if (complex_selector.has_value()) + complex_selectors.append(complex_selector.value()); + consume_whitespace_or_comments(); + if (!peek() || peek() == ',' || peek() == '{') + break; + // HACK: If we didn't move forward, just let go. + if (index == index_before) + break; + } + + if (complex_selectors.is_empty()) + return; + complex_selectors.first().relation = CSS::Selector::ComplexSelector::Relation::None; + + current_rule.selectors.append(CSS::Selector(move(complex_selectors))); + } + + Optional<CSS::Selector> parse_individual_selector() + { + parse_selector(); + if (current_rule.selectors.is_empty()) + return {}; + return current_rule.selectors.last(); + } + + void parse_selector_list() + { + for (;;) { + auto index_before = index; + parse_selector(); + consume_whitespace_or_comments(); + if (peek() == ',') { + consume_one(); + continue; + } + if (peek() == '{') + break; + // HACK: If we didn't move forward, just let go. + if (index_before == index) + break; + } + } + + bool is_valid_property_name_char(char ch) const + { + return ch && !isspace(ch) && ch != ':'; + } + + bool is_valid_property_value_char(char ch) const + { + return ch && ch != '!' && ch != ';' && ch != '}'; + } + + struct ValueAndImportant { + String value; + bool important { false }; + }; + + ValueAndImportant consume_css_value() + { + buffer.clear(); + + int paren_nesting_level = 0; + bool important = false; + + for (;;) { + char ch = peek(); + if (ch == '(') { + ++paren_nesting_level; + buffer.append(consume_one()); + continue; + } + if (ch == ')') { + PARSE_ASSERT(paren_nesting_level > 0); + --paren_nesting_level; + buffer.append(consume_one()); + continue; + } + if (paren_nesting_level > 0) { + buffer.append(consume_one()); + continue; + } + if (next_is("!important")) { + consume_specific('!'); + consume_specific('i'); + consume_specific('m'); + consume_specific('p'); + consume_specific('o'); + consume_specific('r'); + consume_specific('t'); + consume_specific('a'); + consume_specific('n'); + consume_specific('t'); + important = true; + continue; + } + if (next_is("/*")) { + consume_whitespace_or_comments(); + continue; + } + if (!ch) + break; + if (ch == '\\') { + consume_one(); + buffer.append(consume_one()); + continue; + } + if (ch == '}') + break; + if (ch == ';') + break; + buffer.append(consume_one()); + } + + // Remove trailing whitespace. + while (!buffer.is_empty() && isspace(buffer.last())) + buffer.take_last(); + + auto string = String::copy(buffer); + buffer.clear(); + + return { string, important }; + } + + Optional<CSS::StyleProperty> parse_property() + { + consume_whitespace_or_comments(); + if (peek() == ';') { + consume_one(); + return {}; + } + if (peek() == '}') + return {}; + buffer.clear(); + while (is_valid_property_name_char(peek())) + buffer.append(consume_one()); + auto property_name = String::copy(buffer); + buffer.clear(); + consume_whitespace_or_comments(); + if (!consume_specific(':')) + return {}; + consume_whitespace_or_comments(); + + auto [property_value, important] = consume_css_value(); + + consume_whitespace_or_comments(); + + if (peek() && peek() != '}') { + if (!consume_specific(';')) + return {}; + } + + auto property_id = CSS::property_id_from_string(property_name); + if (property_id == CSS::PropertyID::Invalid) { + dbg() << "CSSParser: Unrecognized property '" << property_name << "'"; + } + auto value = parse_css_value(m_context, property_value, property_id); + if (!value) + return {}; + return CSS::StyleProperty { property_id, value.release_nonnull(), important }; + } + + void parse_declaration() + { + for (;;) { + auto property = parse_property(); + if (property.has_value()) + current_rule.properties.append(property.value()); + consume_whitespace_or_comments(); + if (!peek() || peek() == '}') + break; + } + } + + void parse_rule() + { + consume_whitespace_or_comments(); + if (!peek()) + return; + + // FIXME: We ignore @-rules for now. + if (peek() == '@') { + while (peek() != '{') + consume_one(); + int level = 0; + for (;;) { + auto ch = consume_one(); + if (ch == '{') { + ++level; + } else if (ch == '}') { + --level; + if (level == 0) + break; + } + } + consume_whitespace_or_comments(); + return; + } + + parse_selector_list(); + if (!consume_specific('{')) { + PARSE_ERROR(); + return; + } + parse_declaration(); + if (!consume_specific('}')) { + PARSE_ERROR(); + return; + } + rules.append(CSS::StyleRule::create(move(current_rule.selectors), CSS::StyleDeclaration::create(move(current_rule.properties)))); + consume_whitespace_or_comments(); + } + + RefPtr<CSS::StyleSheet> parse_sheet() + { + if (peek(0) == (char)0xef && peek(1) == (char)0xbb && peek(2) == (char)0xbf) { + // HACK: Skip UTF-8 BOM. + index += 3; + } + + while (peek()) { + parse_rule(); + } + + return CSS::StyleSheet::create(move(rules)); + } + + RefPtr<CSS::StyleDeclaration> parse_standalone_declaration() + { + consume_whitespace_or_comments(); + for (;;) { + auto property = parse_property(); + if (property.has_value()) + current_rule.properties.append(property.value()); + consume_whitespace_or_comments(); + if (!peek()) + break; + } + return CSS::StyleDeclaration::create(move(current_rule.properties)); + } + +private: + CSS::ParsingContext m_context; + + NonnullRefPtrVector<CSS::StyleRule> rules; + + struct CurrentRule { + Vector<CSS::Selector> selectors; + Vector<CSS::StyleProperty> properties; + }; + + CurrentRule current_rule; + Vector<char> buffer; + + size_t index = 0; + + StringView css; +}; + +Optional<CSS::Selector> parse_selector(const CSS::ParsingContext& context, const StringView& selector_text) +{ + CSSParser parser(context, selector_text); + return parser.parse_individual_selector(); +} + +RefPtr<CSS::StyleSheet> parse_css(const CSS::ParsingContext& context, const StringView& css) +{ + if (css.is_empty()) + return CSS::StyleSheet::create({}); + CSSParser parser(context, css); + return parser.parse_sheet(); +} + +RefPtr<CSS::StyleDeclaration> parse_css_declaration(const CSS::ParsingContext& context, const StringView& css) +{ + if (css.is_empty()) + return CSS::StyleDeclaration::create({}); + CSSParser parser(context, css); + return parser.parse_standalone_declaration(); +} + +RefPtr<CSS::StyleValue> parse_html_length(const DOM::Document& document, const StringView& string) +{ + auto integer = string.to_int(); + if (integer.has_value()) + return CSS::LengthStyleValue::create(CSS::Length::make_px(integer.value())); + return parse_css_value(CSS::ParsingContext(document), string); +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/Parser/CSSParser.h b/Userland/Libraries/LibWeb/CSS/Parser/CSSParser.h new file mode 100644 index 0000000000..ca9a4688e2 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Parser/CSSParser.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018-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 <AK/NonnullRefPtr.h> +#include <LibWeb/CSS/StyleSheet.h> + +namespace Web::CSS { +class ParsingContext { +public: + ParsingContext(); + explicit ParsingContext(const DOM::Document&); + explicit ParsingContext(const DOM::ParentNode&); + + bool in_quirks_mode() const; + +private: + const DOM::Document* m_document { nullptr }; +}; +} + +namespace Web { + +RefPtr<CSS::StyleSheet> parse_css(const CSS::ParsingContext&, const StringView&); +RefPtr<CSS::StyleDeclaration> parse_css_declaration(const CSS::ParsingContext&, const StringView&); +RefPtr<CSS::StyleValue> parse_css_value(const CSS::ParsingContext&, const StringView&, CSS::PropertyID property_id = CSS::PropertyID::Invalid); +Optional<CSS::Selector> parse_selector(const CSS::ParsingContext&, const StringView&); + +RefPtr<CSS::LengthStyleValue> parse_line_width(const CSS::ParsingContext&, const StringView&); +RefPtr<CSS::ColorStyleValue> parse_color(const CSS::ParsingContext&, const StringView&); +RefPtr<CSS::StringStyleValue> parse_line_style(const CSS::ParsingContext&, const StringView&); + +RefPtr<CSS::StyleValue> parse_html_length(const DOM::Document&, const StringView&); + +} diff --git a/Userland/Libraries/LibWeb/CSS/Properties.json b/Userland/Libraries/LibWeb/CSS/Properties.json new file mode 100644 index 0000000000..86c4009834 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Properties.json @@ -0,0 +1,368 @@ +{ + "background": { + }, + "background-attachment": { + "inherited": false, + "initial": "scroll" + }, + "background-color": { + "inherited": false, + "initial": "transparent" + }, + "background-image": { + "inherited": false, + "initial": "none" + }, + "background-position": { + "inherited": false, + "initial": "0% 0%" + }, + "background-repeat": { + "inherited": false, + "initial": "repeat" + }, + "border": { + "longhands": [ + "border-width", + "border-style", + "border-color" + ] + }, + "border-top": { + "longhands": [ + "border-top-width", + "border-top-style", + "border-top-color" + ] + }, + "border-right": { + "longhands": [ + "border-right-width", + "border-right-style", + "border-right-color" + ] + }, + "border-bottom": { + "longhands": [ + "border-bottom-width", + "border-bottom-style", + "border-bottom-color" + ] + }, + "border-left": { + "longhands": [ + "border-left-width", + "border-left-style", + "border-left-color" + ] + }, + "border-bottom-color": { + "initial": "currentColor", + "inherited": false + }, + "border-bottom-style": { + "initial": "none", + "inherited": false + }, + "border-bottom-width": { + "initial": "medium", + "inherited": false + }, + "border-color": { + "longhands": [ + "border-top-color", + "border-right-color", + "border-bottom-color", + "border-left-color" + ] + }, + "border-collapse": { + "inherited": true, + "initial": "separate" + }, + "border-left-color": { + "initial": "currentColor", + "inherited": false + }, + "border-left-style": { + "initial": "none", + "inherited": false + }, + "border-left-width": { + "initial": "medium", + "inherited": false + }, + "border-right-color": { + "initial": "currentColor", + "inherited": false + }, + "border-right-style": { + "initial": "none", + "inherited": false + }, + "border-right-width": { + "initial": "medium", + "inherited": false + }, + "border-spacing": { + "inherited": true, + "initial": "0" + }, + "border-style": { + "longhands": [ + "border-top-style", + "border-right-style", + "border-bottom-style", + "border-left-style" + ] + }, + "border-top-color": { + "initial": "currentColor", + "inherited": false + }, + "border-top-style": { + "initial": "none", + "inherited": false + }, + "border-top-width": { + "initial": "medium", + "inherited": false + }, + "border-width": { + "longhands": [ + "border-top-width", + "border-right-width", + "border-bottom-width", + "border-left-width" + ] + }, + "bottom": { + "inherited": false, + "initial": "auto" + }, + "caption-side": { + "inherited": true, + "initial": "top" + }, + "clear": { + "inherited": false, + "initial": "none" + }, + "clip": { + "inherited": true, + "initial": "auto" + }, + "color": { + "inherited": true, + "initial": "" + }, + "cursor": { + "inherited": true, + "initial": "auto" + }, + "direction": { + "inherited": true, + "initial": "ltr" + }, + "display": { + "inherited": false, + "initial": "inline" + }, + "float": { + "inherited": false, + "initial": "none" + }, + "font-family": { + "inherited": true, + "initial": "sans-serif" + }, + "font-size": { + "inherited": true, + "initial": "medium" + }, + "font-style": { + "inherited": true, + "initial": "normal" + }, + "font-variant": { + "inherited": true, + "initial": "normal" + }, + "font-weight": { + "inherited": true, + "initial": "normal" + }, + "height": { + "inherited": false, + "initial": "auto" + }, + "left": { + "inherited": false, + "initial": "auto" + }, + "letter-spacing": { + "inherited": true, + "initial": "normal" + }, + "line-height": { + "inherited": true, + "initial": "normal" + }, + "list-style": { + "longhands": [ + "list-style-type", + "list-style-position", + "list-style-image" + ] + }, + "list-style-image": { + "inherited": true, + "initial": "none" + }, + "list-style-position": { + "inherited": true, + "initial": "outside" + }, + "list-style-type": { + "inherited": true, + "initial": "disc" + }, + "margin": { + "longhands": [ + "margin-top", + "margin-right", + "margin-bottom", + "margin-left" + ] + }, + "margin-bottom": { + "inherited": false, + "initial": "0" + }, + "margin-left": { + "inherited": false, + "initial": "0" + }, + "margin-right": { + "inherited": false, + "initial": "0" + }, + "margin-top": { + "inherited": false, + "initial": "0" + }, + "max-height": { + "inherited": false, + "initial": "none" + }, + "max-width": { + "inherited": false, + "initial": "none" + }, + "min-height": { + "inherited": false, + "initial": "0" + }, + "min-width": { + "inherited": false, + "initial": "0" + }, + "padding": { + "longhands": [ + "padding-top", + "padding-right", + "padding-bottom", + "padding-left" + ] + }, + "padding-bottom": { + "inherited": false, + "initial": "0" + }, + "padding-left": { + "inherited": false, + "initial": "0" + }, + "padding-right": { + "inherited": false, + "initial": "0" + }, + "padding-top": { + "inherited": false, + "initial": "0" + }, + "position": { + "inherited": false, + "initial": "static" + }, + "right": { + "inherited": false, + "initial": "auto" + }, + "text-align": { + "inherited": true, + "initial": "left" + }, + "text-decoration": { + "inherited": false, + "initial": "none", + "longhands": [ + "text-decoration-color", + "text-decoration-line", + "text-decoration-style", + "text-decoration-thickness" + ] + }, + "text-decoration-color": { + "inherited": false, + "initial": "none" + }, + "text-decoration-line": { + "inherited": false, + "initial": "none" + }, + "text-decoration-style": { + "inherited": false, + "initial": "none" + }, + "text-decoration-thickness": { + "inherited": false, + "initial": "none" + }, + "text-indent": { + "inherited": true, + "initial": "0" + }, + "text-transform": { + "inherited": true, + "initial": "none" + }, + "top": { + "inherited": false, + "initial": "auto" + }, + "vertical-align": { + "inherited": false, + "initial": "baseline" + }, + "visibility": { + "inherited": true, + "initial": "visible" + }, + "width": { + "inherited": false, + "initial": "auto" + }, + "white-space": { + "inherited": true, + "initial": "normal" + }, + "word-spacing": { + "inherited": true, + "initial": "normal" + }, + "z-index": { + "inherited": false, + "initial": "auto" + } +} diff --git a/Userland/Libraries/LibWeb/CSS/QuirksMode.css b/Userland/Libraries/LibWeb/CSS/QuirksMode.css new file mode 100644 index 0000000000..8e1aeb392b --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/QuirksMode.css @@ -0,0 +1,3 @@ +table { + text-align: left; +} diff --git a/Userland/Libraries/LibWeb/CSS/Selector.cpp b/Userland/Libraries/LibWeb/CSS/Selector.cpp new file mode 100644 index 0000000000..bfd44f204a --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Selector.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018-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 <LibWeb/CSS/Selector.h> + +namespace Web::CSS { + +Selector::Selector(Vector<ComplexSelector>&& component_lists) + : m_complex_selectors(move(component_lists)) +{ +} + +Selector::~Selector() +{ +} + +u32 Selector::specificity() const +{ + unsigned ids = 0; + unsigned tag_names = 0; + unsigned classes = 0; + + for (auto& list : m_complex_selectors) { + for (auto& simple_selector : list.compound_selector) { + switch (simple_selector.type) { + case SimpleSelector::Type::Id: + ++ids; + break; + case SimpleSelector::Type::Class: + ++classes; + break; + case SimpleSelector::Type::TagName: + ++tag_names; + break; + default: + break; + } + } + } + + return ids * 0x10000 + classes * 0x100 + tag_names; +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/Selector.h b/Userland/Libraries/LibWeb/CSS/Selector.h new file mode 100644 index 0000000000..d5dae94f98 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Selector.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018-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 <AK/FlyString.h> +#include <AK/Vector.h> + +namespace Web::CSS { + +class Selector { +public: + struct SimpleSelector { + enum class Type { + Invalid, + Universal, + TagName, + Id, + Class, + }; + Type type { Type::Invalid }; + + enum class PseudoClass { + None, + Link, + Visited, + Hover, + Focus, + FirstChild, + LastChild, + OnlyChild, + Empty, + Root, + }; + PseudoClass pseudo_class { PseudoClass::None }; + + enum class PseudoElement { + None, + Before, + After, + }; + PseudoElement pseudo_element { PseudoElement::None }; + + FlyString value; + + enum class AttributeMatchType { + None, + HasAttribute, + ExactValueMatch, + Contains, + }; + + AttributeMatchType attribute_match_type { AttributeMatchType::None }; + FlyString attribute_name; + String attribute_value; + }; + + struct ComplexSelector { + enum class Relation { + None, + ImmediateChild, + Descendant, + AdjacentSibling, + GeneralSibling, + }; + Relation relation { Relation::None }; + + using CompoundSelector = Vector<SimpleSelector>; + CompoundSelector compound_selector; + }; + + explicit Selector(Vector<ComplexSelector>&&); + ~Selector(); + + const Vector<ComplexSelector>& complex_selectors() const { return m_complex_selectors; } + + u32 specificity() const; + +private: + Vector<ComplexSelector> m_complex_selectors; +}; + +} diff --git a/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp b/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp new file mode 100644 index 0000000000..5f553e2be0 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2018-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 <LibWeb/CSS/SelectorEngine.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/DOM/Text.h> +#include <LibWeb/HTML/AttributeNames.h> +#include <LibWeb/HTML/HTMLElement.h> + +namespace Web::SelectorEngine { + +static bool matches_hover_pseudo_class(const DOM::Element& element) +{ + auto* hovered_node = element.document().hovered_node(); + if (!hovered_node) + return false; + if (&element == hovered_node) + return true; + return element.is_ancestor_of(*hovered_node); +} + +static bool matches(const CSS::Selector::SimpleSelector& component, const DOM::Element& element) +{ + switch (component.pseudo_element) { + case CSS::Selector::SimpleSelector::PseudoElement::None: + break; + default: + // FIXME: Implement pseudo-elements. + return false; + } + + switch (component.pseudo_class) { + case CSS::Selector::SimpleSelector::PseudoClass::None: + break; + case CSS::Selector::SimpleSelector::PseudoClass::Link: + if (!element.is_link()) + return false; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Visited: + // FIXME: Maybe match this selector sometimes? + return false; + case CSS::Selector::SimpleSelector::PseudoClass::Hover: + if (!matches_hover_pseudo_class(element)) + return false; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Focus: + // FIXME: Implement matches_focus_pseudo_class(element) + return false; + case CSS::Selector::SimpleSelector::PseudoClass::FirstChild: + if (element.previous_element_sibling()) + return false; + break; + case CSS::Selector::SimpleSelector::PseudoClass::LastChild: + if (element.next_element_sibling()) + return false; + break; + case CSS::Selector::SimpleSelector::PseudoClass::OnlyChild: + if (element.previous_element_sibling() || element.next_element_sibling()) + return false; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Empty: + if (element.first_child_of_type<DOM::Element>() || element.first_child_of_type<DOM::Text>()) + return false; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Root: + if (!is<HTML::HTMLElement>(element)) + return false; + break; + } + + switch (component.attribute_match_type) { + case CSS::Selector::SimpleSelector::AttributeMatchType::HasAttribute: + if (!element.has_attribute(component.attribute_name)) + return false; + break; + case CSS::Selector::SimpleSelector::AttributeMatchType::ExactValueMatch: + if (element.attribute(component.attribute_name) != component.attribute_value) + return false; + break; + case CSS::Selector::SimpleSelector::AttributeMatchType::Contains: + if (!element.attribute(component.attribute_name).split(' ').contains_slow(component.attribute_value)) + return false; + break; + default: + break; + } + + switch (component.type) { + case CSS::Selector::SimpleSelector::Type::Universal: + return true; + case CSS::Selector::SimpleSelector::Type::Id: + return component.value == element.attribute(HTML::AttributeNames::id); + case CSS::Selector::SimpleSelector::Type::Class: + return element.has_class(component.value); + case CSS::Selector::SimpleSelector::Type::TagName: + return component.value == element.local_name(); + default: + ASSERT_NOT_REACHED(); + } +} + +static bool matches(const CSS::Selector& selector, int component_list_index, const DOM::Element& element) +{ + auto& component_list = selector.complex_selectors()[component_list_index]; + for (auto& component : component_list.compound_selector) { + if (!matches(component, element)) + return false; + } + switch (component_list.relation) { + case CSS::Selector::ComplexSelector::Relation::None: + return true; + case CSS::Selector::ComplexSelector::Relation::Descendant: + ASSERT(component_list_index != 0); + for (auto* ancestor = element.parent(); ancestor; ancestor = ancestor->parent()) { + if (!is<DOM::Element>(*ancestor)) + continue; + if (matches(selector, component_list_index - 1, downcast<DOM::Element>(*ancestor))) + return true; + } + return false; + case CSS::Selector::ComplexSelector::Relation::ImmediateChild: + ASSERT(component_list_index != 0); + if (!element.parent() || !is<DOM::Element>(*element.parent())) + return false; + return matches(selector, component_list_index - 1, downcast<DOM::Element>(*element.parent())); + case CSS::Selector::ComplexSelector::Relation::AdjacentSibling: + ASSERT(component_list_index != 0); + if (auto* sibling = element.previous_element_sibling()) + return matches(selector, component_list_index - 1, *sibling); + return false; + case CSS::Selector::ComplexSelector::Relation::GeneralSibling: + ASSERT(component_list_index != 0); + for (auto* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) { + if (matches(selector, component_list_index - 1, *sibling)) + return true; + } + return false; + } + ASSERT_NOT_REACHED(); +} + +bool matches(const CSS::Selector& selector, const DOM::Element& element) +{ + ASSERT(!selector.complex_selectors().is_empty()); + return matches(selector, selector.complex_selectors().size() - 1, element); +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/SelectorEngine.h b/Userland/Libraries/LibWeb/CSS/SelectorEngine.h new file mode 100644 index 0000000000..96bd76de36 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/SelectorEngine.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-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/CSS/Selector.h> +#include <LibWeb/DOM/Element.h> + +namespace Web::SelectorEngine { + +bool matches(const CSS::Selector&, const DOM::Element&); + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleDeclaration.cpp b/Userland/Libraries/LibWeb/CSS/StyleDeclaration.cpp new file mode 100644 index 0000000000..eb56b94625 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleDeclaration.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-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 <LibWeb/CSS/StyleDeclaration.h> + +namespace Web::CSS { + +StyleDeclaration::StyleDeclaration(Vector<StyleProperty>&& properties) + : m_properties(move(properties)) +{ +} + +StyleDeclaration::~StyleDeclaration() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleDeclaration.h b/Userland/Libraries/LibWeb/CSS/StyleDeclaration.h new file mode 100644 index 0000000000..787429eb15 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleDeclaration.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-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 <AK/String.h> +#include <AK/Vector.h> +#include <LibWeb/CSS/StyleValue.h> + +namespace Web::CSS { + +struct StyleProperty { + CSS::PropertyID property_id; + NonnullRefPtr<StyleValue> value; + bool important { false }; +}; + +class StyleDeclaration : public RefCounted<StyleDeclaration> { +public: + static NonnullRefPtr<StyleDeclaration> create(Vector<StyleProperty>&& properties) + { + return adopt(*new StyleDeclaration(move(properties))); + } + + ~StyleDeclaration(); + + const Vector<StyleProperty>& properties() const { return m_properties; } + +private: + explicit StyleDeclaration(Vector<StyleProperty>&&); + + Vector<StyleProperty> m_properties; +}; + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleInvalidator.cpp b/Userland/Libraries/LibWeb/CSS/StyleInvalidator.cpp new file mode 100644 index 0000000000..bf5f15c332 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleInvalidator.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020, Linus Groh <mail@linusgroh.de> + * 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 <LibWeb/CSS/StyleInvalidator.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Element.h> + +namespace Web::CSS { + +StyleInvalidator::StyleInvalidator(DOM::Document& document) + : m_document(document) +{ + if (!m_document.should_invalidate_styles_on_attribute_changes()) + return; + auto& style_resolver = m_document.style_resolver(); + m_document.for_each_in_subtree_of_type<DOM::Element>([&](auto& element) { + m_elements_and_matching_rules_before.set(&element, style_resolver.collect_matching_rules(element)); + return IterationDecision::Continue; + }); +} + +StyleInvalidator::~StyleInvalidator() +{ + if (!m_document.should_invalidate_styles_on_attribute_changes()) + return; + auto& style_resolver = m_document.style_resolver(); + m_document.for_each_in_subtree_of_type<DOM::Element>([&](auto& element) { + auto maybe_matching_rules_before = m_elements_and_matching_rules_before.get(&element); + if (!maybe_matching_rules_before.has_value()) { + element.set_needs_style_update(true); + return IterationDecision::Continue; + } + auto& matching_rules_before = maybe_matching_rules_before.value(); + auto matching_rules_after = style_resolver.collect_matching_rules(element); + if (matching_rules_before.size() != matching_rules_after.size()) { + element.set_needs_style_update(true); + return IterationDecision::Continue; + } + style_resolver.sort_matching_rules(matching_rules_before); + style_resolver.sort_matching_rules(matching_rules_after); + for (size_t i = 0; i < matching_rules_before.size(); ++i) { + if (matching_rules_before[i].rule != matching_rules_after[i].rule) { + element.set_needs_style_update(true); + break; + } + } + return IterationDecision::Continue; + }); +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleInvalidator.h b/Userland/Libraries/LibWeb/CSS/StyleInvalidator.h new file mode 100644 index 0000000000..aea59c8e65 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleInvalidator.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, Linus Groh <mail@linusgroh.de> + * 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 <AK/HashMap.h> +#include <LibWeb/CSS/StyleResolver.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Element.h> + +namespace Web::CSS { + +class StyleInvalidator { +public: + explicit StyleInvalidator(DOM::Document&); + ~StyleInvalidator(); + +private: + DOM::Document& m_document; + HashMap<DOM::Element*, Vector<MatchingRule>> m_elements_and_matching_rules_before; +}; + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp new file mode 100644 index 0000000000..76b2834771 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2018-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 <LibCore/DirIterator.h> +#include <LibGfx/FontDatabase.h> +#include <LibWeb/CSS/StyleProperties.h> +#include <LibWeb/FontCache.h> +#include <ctype.h> + +namespace Web::CSS { + +StyleProperties::StyleProperties() +{ +} + +StyleProperties::StyleProperties(const StyleProperties& other) + : m_property_values(other.m_property_values) +{ + if (other.m_font) { + m_font = other.m_font->clone(); + } else { + m_font = nullptr; + } +} + +NonnullRefPtr<StyleProperties> StyleProperties::clone() const +{ + return adopt(*new StyleProperties(*this)); +} + +void StyleProperties::set_property(CSS::PropertyID id, NonnullRefPtr<StyleValue> value) +{ + m_property_values.set((unsigned)id, move(value)); +} + +void StyleProperties::set_property(CSS::PropertyID id, const StringView& value) +{ + m_property_values.set((unsigned)id, StringStyleValue::create(value)); +} + +Optional<NonnullRefPtr<StyleValue>> StyleProperties::property(CSS::PropertyID id) const +{ + auto it = m_property_values.find((unsigned)id); + if (it == m_property_values.end()) + return {}; + return it->value; +} + +Length StyleProperties::length_or_fallback(CSS::PropertyID id, const Length& fallback) const +{ + auto value = property(id); + if (!value.has_value()) + return fallback; + return value.value()->to_length(); +} + +LengthBox StyleProperties::length_box(CSS::PropertyID left_id, CSS::PropertyID top_id, CSS::PropertyID right_id, CSS::PropertyID bottom_id, const CSS::Length& default_value) const +{ + LengthBox box; + box.left = length_or_fallback(left_id, default_value); + box.top = length_or_fallback(top_id, default_value); + box.right = length_or_fallback(right_id, default_value); + box.bottom = length_or_fallback(bottom_id, default_value); + return box; +} + +String StyleProperties::string_or_fallback(CSS::PropertyID id, const StringView& fallback) const +{ + auto value = property(id); + if (!value.has_value()) + return fallback; + return value.value()->to_string(); +} + +Color StyleProperties::color_or_fallback(CSS::PropertyID id, const DOM::Document& document, Color fallback) const +{ + auto value = property(id); + if (!value.has_value()) + return fallback; + return value.value()->to_color(document); +} + +void StyleProperties::load_font() const +{ + auto family_value = string_or_fallback(CSS::PropertyID::FontFamily, "Katica"); + auto font_size = property(CSS::PropertyID::FontSize).value_or(IdentifierStyleValue::create(CSS::ValueID::Medium)); + auto font_weight = property(CSS::PropertyID::FontWeight).value_or(IdentifierStyleValue::create(CSS::ValueID::Normal)); + + auto family_parts = family_value.split(','); + auto family = family_parts[0]; + + if (family.is_one_of("monospace", "ui-monospace")) + family = "Csilla"; + else if (family.is_one_of("serif", "sans-serif", "cursive", "fantasy", "ui-serif", "ui-sans-serif", "ui-rounded")) + family = "Katica"; + + int weight = 400; + if (font_weight->is_identifier()) { + switch (static_cast<const IdentifierStyleValue&>(*font_weight).id()) { + case CSS::ValueID::Normal: + weight = 400; + break; + case CSS::ValueID::Bold: + weight = 700; + break; + case CSS::ValueID::Lighter: + // FIXME: This should be relative to the parent. + weight = 400; + break; + case CSS::ValueID::Bolder: + // FIXME: This should be relative to the parent. + weight = 700; + break; + default: + break; + } + } else if (font_weight->is_length()) { + // FIXME: This isn't really a length, it's a numeric value.. + int font_weight_integer = font_weight->to_length().raw_value(); + if (font_weight_integer <= 400) + weight = 400; + if (font_weight_integer <= 700) + weight = 700; + weight = 900; + } + + int size = 10; + if (font_size->is_identifier()) { + switch (static_cast<const IdentifierStyleValue&>(*font_size).id()) { + case CSS::ValueID::XxSmall: + case CSS::ValueID::XSmall: + case CSS::ValueID::Small: + case CSS::ValueID::Medium: + // FIXME: Should be based on "user's default font size" + size = 10; + break; + case CSS::ValueID::Large: + case CSS::ValueID::XLarge: + case CSS::ValueID::XxLarge: + case CSS::ValueID::XxxLarge: + // FIXME: Should be based on "user's default font size" + size = 12; + break; + case CSS::ValueID::Smaller: + // FIXME: This should be relative to the parent. + size = 10; + break; + case CSS::ValueID::Larger: + // FIXME: This should be relative to the parent. + size = 12; + break; + + default: + break; + } + } else if (font_size->is_length()) { + // FIXME: This isn't really a length, it's a numeric value.. + int font_size_integer = font_size->to_length().raw_value(); + if (font_size_integer <= 10) + size = 10; + else if (font_size_integer <= 12) + size = 12; + else + size = 14; + } + + FontSelector font_selector { family, size, weight }; + + auto found_font = FontCache::the().get(font_selector); + if (found_font) { + m_font = found_font; + return; + } + + Gfx::FontDatabase::the().for_each_font([&](auto& font) { + if (font.family() == family && font.weight() == weight && font.presentation_size() == size) + found_font = font; + }); + + if (!found_font) { + dbgln("Font not found: '{}' {} {}", family, size, weight); + found_font = Gfx::FontDatabase::default_font(); + } + + m_font = found_font; + FontCache::the().set(font_selector, *m_font); +} + +float StyleProperties::line_height(const Layout::Node& layout_node) const +{ + auto line_height_length = length_or_fallback(CSS::PropertyID::LineHeight, Length::make_auto()); + if (line_height_length.is_absolute()) + return (float)line_height_length.to_px(layout_node); + return (float)font().glyph_height() * 1.4f; +} + +Optional<int> StyleProperties::z_index() const +{ + auto value = property(CSS::PropertyID::ZIndex); + if (!value.has_value()) + return {}; + return static_cast<int>(value.value()->to_length().raw_value()); +} + +Optional<CSS::Position> StyleProperties::position() const +{ + auto value = property(CSS::PropertyID::Position); + if (!value.has_value() || !value.value()->is_identifier()) + return {}; + switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) { + case CSS::ValueID::Static: + return CSS::Position::Static; + case CSS::ValueID::Relative: + return CSS::Position::Relative; + case CSS::ValueID::Absolute: + return CSS::Position::Absolute; + case CSS::ValueID::Fixed: + return CSS::Position::Fixed; + case CSS::ValueID::Sticky: + return CSS::Position::Sticky; + default: + return {}; + } +} + +bool StyleProperties::operator==(const StyleProperties& other) const +{ + if (m_property_values.size() != other.m_property_values.size()) + return false; + + for (auto& it : m_property_values) { + auto jt = other.m_property_values.find(it.key); + if (jt == other.m_property_values.end()) + return false; + auto& my_value = *it.value; + auto& other_value = *jt->value; + if (my_value.type() != other_value.type()) + return false; + if (my_value != other_value) + return false; + } + + return true; +} + +Optional<CSS::TextAlign> StyleProperties::text_align() const +{ + auto value = property(CSS::PropertyID::TextAlign); + if (!value.has_value() || !value.value()->is_identifier()) + return {}; + + switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) { + case CSS::ValueID::Left: + return CSS::TextAlign::Left; + case CSS::ValueID::Center: + return CSS::TextAlign::Center; + case CSS::ValueID::Right: + return CSS::TextAlign::Right; + case CSS::ValueID::Justify: + return CSS::TextAlign::Justify; + case CSS::ValueID::LibwebCenter: + return CSS::TextAlign::LibwebCenter; + default: + return {}; + } +} + +Optional<CSS::WhiteSpace> StyleProperties::white_space() const +{ + auto value = property(CSS::PropertyID::WhiteSpace); + if (!value.has_value() || !value.value()->is_identifier()) + return {}; + switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) { + case CSS::ValueID::Normal: + return CSS::WhiteSpace::Normal; + case CSS::ValueID::Nowrap: + return CSS::WhiteSpace::Nowrap; + case CSS::ValueID::Pre: + return CSS::WhiteSpace::Pre; + case CSS::ValueID::PreLine: + return CSS::WhiteSpace::PreLine; + case CSS::ValueID::PreWrap: + return CSS::WhiteSpace::PreWrap; + default: + return {}; + } +} + +Optional<CSS::LineStyle> StyleProperties::line_style(CSS::PropertyID property_id) const +{ + auto value = property(property_id); + if (!value.has_value() || !value.value()->is_identifier()) + return {}; + switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) { + case CSS::ValueID::None: + return CSS::LineStyle::None; + case CSS::ValueID::Hidden: + return CSS::LineStyle::Hidden; + case CSS::ValueID::Dotted: + return CSS::LineStyle::Dotted; + case CSS::ValueID::Dashed: + return CSS::LineStyle::Dashed; + case CSS::ValueID::Solid: + return CSS::LineStyle::Solid; + case CSS::ValueID::Double: + return CSS::LineStyle::Double; + case CSS::ValueID::Groove: + return CSS::LineStyle::Groove; + case CSS::ValueID::Ridge: + return CSS::LineStyle::Ridge; + case CSS::ValueID::Inset: + return CSS::LineStyle::Inset; + case CSS::ValueID::Outset: + return CSS::LineStyle::Outset; + default: + return {}; + } +} + +Optional<CSS::Float> StyleProperties::float_() const +{ + auto value = property(CSS::PropertyID::Float); + if (!value.has_value() || !value.value()->is_identifier()) + return {}; + switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) { + case CSS::ValueID::None: + return CSS::Float::None; + case CSS::ValueID::Left: + return CSS::Float::Left; + case CSS::ValueID::Right: + return CSS::Float::Right; + default: + return {}; + } +} + +Optional<CSS::Clear> StyleProperties::clear() const +{ + auto value = property(CSS::PropertyID::Clear); + if (!value.has_value() || !value.value()->is_identifier()) + return {}; + switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) { + case CSS::ValueID::None: + return CSS::Clear::None; + case CSS::ValueID::Left: + return CSS::Clear::Left; + case CSS::ValueID::Right: + return CSS::Clear::Right; + case CSS::ValueID::Both: + return CSS::Clear::Both; + default: + return {}; + } +} + +CSS::Display StyleProperties::display() const +{ + auto value = property(CSS::PropertyID::Display); + if (!value.has_value() || !value.value()->is_identifier()) + return CSS::Display::Inline; + switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) { + case CSS::ValueID::None: + return CSS::Display::None; + case CSS::ValueID::Block: + return CSS::Display::Block; + case CSS::ValueID::Inline: + return CSS::Display::Inline; + case CSS::ValueID::InlineBlock: + return CSS::Display::InlineBlock; + case CSS::ValueID::ListItem: + return CSS::Display::ListItem; + case CSS::ValueID::Table: + return CSS::Display::Table; + case CSS::ValueID::TableRow: + return CSS::Display::TableRow; + case CSS::ValueID::TableCell: + return CSS::Display::TableCell; + case CSS::ValueID::TableColumn: + return CSS::Display::TableColumn; + case CSS::ValueID::TableColumnGroup: + return CSS::Display::TableColumnGroup; + case CSS::ValueID::TableCaption: + return CSS::Display::TableCaption; + case CSS::ValueID::TableRowGroup: + return CSS::Display::TableRowGroup; + case CSS::ValueID::TableHeaderGroup: + return CSS::Display::TableHeaderGroup; + case CSS::ValueID::TableFooterGroup: + return CSS::Display::TableFooterGroup; + default: + return CSS::Display::Block; + } +} + +Optional<CSS::TextDecorationLine> StyleProperties::text_decoration_line() const +{ + auto value = property(CSS::PropertyID::TextDecorationLine); + if (!value.has_value() || !value.value()->is_identifier()) + return {}; + switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) { + case CSS::ValueID::None: + return CSS::TextDecorationLine::None; + case CSS::ValueID::Underline: + return CSS::TextDecorationLine::Underline; + case CSS::ValueID::Overline: + return CSS::TextDecorationLine::Overline; + case CSS::ValueID::LineThrough: + return CSS::TextDecorationLine::LineThrough; + case CSS::ValueID::Blink: + return CSS::TextDecorationLine::Blink; + default: + return {}; + } +} + +Optional<CSS::TextTransform> StyleProperties::text_transform() const +{ + auto value = property(CSS::PropertyID::TextTransform); + if (!value.has_value()) + return {}; + switch (value.value()->to_identifier()) { + case CSS::ValueID::None: + return CSS::TextTransform::None; + case CSS::ValueID::Lowercase: + return CSS::TextTransform::Lowercase; + case CSS::ValueID::Uppercase: + return CSS::TextTransform::Uppercase; + case CSS::ValueID::Capitalize: + return CSS::TextTransform::Capitalize; + case CSS::ValueID::FullWidth: + return CSS::TextTransform::FullWidth; + case CSS::ValueID::FullSizeKana: + return CSS::TextTransform::FullSizeKana; + default: + return {}; + } +} + +Optional<CSS::ListStyleType> StyleProperties::list_style_type() const +{ + auto value = property(CSS::PropertyID::ListStyleType); + if (!value.has_value()) + return {}; + + switch (value.value()->to_identifier()) { + case CSS::ValueID::None: + return CSS::ListStyleType::None; + case CSS::ValueID::Disc: + return CSS::ListStyleType::Disc; + case CSS::ValueID::Circle: + return CSS::ListStyleType::Circle; + case CSS::ValueID::Square: + return CSS::ListStyleType::Square; + case CSS::ValueID::Decimal: + return CSS::ListStyleType::Decimal; + default: + return {}; + } +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h new file mode 100644 index 0000000000..29da1ab073 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018-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 <AK/HashMap.h> +#include <AK/NonnullRefPtr.h> +#include <LibGfx/Font.h> +#include <LibGfx/Forward.h> +#include <LibWeb/CSS/LengthBox.h> +#include <LibWeb/CSS/StyleValue.h> + +namespace Web::CSS { + +class StyleProperties : public RefCounted<StyleProperties> { +public: + StyleProperties(); + + explicit StyleProperties(const StyleProperties&); + + static NonnullRefPtr<StyleProperties> create() { return adopt(*new StyleProperties); } + + NonnullRefPtr<StyleProperties> clone() const; + + template<typename Callback> + inline void for_each_property(Callback callback) const + { + for (auto& it : m_property_values) + callback((CSS::PropertyID)it.key, *it.value); + } + + void set_property(CSS::PropertyID, NonnullRefPtr<StyleValue> value); + void set_property(CSS::PropertyID, const StringView&); + Optional<NonnullRefPtr<StyleValue>> property(CSS::PropertyID) const; + + Length length_or_fallback(CSS::PropertyID, const Length& fallback) const; + LengthBox length_box(CSS::PropertyID left_id, CSS::PropertyID top_id, CSS::PropertyID right_id, CSS::PropertyID bottom_id, const CSS::Length& default_value) const; + String string_or_fallback(CSS::PropertyID, const StringView& fallback) const; + Color color_or_fallback(CSS::PropertyID, const DOM::Document&, Color fallback) const; + Optional<CSS::TextAlign> text_align() const; + CSS::Display display() const; + Optional<CSS::Float> float_() const; + Optional<CSS::Clear> clear() const; + Optional<CSS::WhiteSpace> white_space() const; + Optional<CSS::LineStyle> line_style(CSS::PropertyID) const; + Optional<CSS::TextDecorationLine> text_decoration_line() const; + Optional<CSS::TextTransform> text_transform() const; + Optional<CSS::ListStyleType> list_style_type() const; + + const Gfx::Font& font() const + { + if (!m_font) + load_font(); + return *m_font; + } + + float line_height(const Layout::Node&) const; + + bool operator==(const StyleProperties&) const; + bool operator!=(const StyleProperties& other) const { return !(*this == other); } + + Optional<CSS::Position> position() const; + Optional<int> z_index() const; + +private: + HashMap<unsigned, NonnullRefPtr<StyleValue>> m_property_values; + + void load_font() const; + + mutable RefPtr<Gfx::Font> m_font; +}; + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp b/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp new file mode 100644 index 0000000000..21715ee92b --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp @@ -0,0 +1,598 @@ +/* + * Copyright (c) 2018-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/QuickSort.h> +#include <LibWeb/CSS/Parser/CSSParser.h> +#include <LibWeb/CSS/SelectorEngine.h> +#include <LibWeb/CSS/StyleResolver.h> +#include <LibWeb/CSS/StyleSheet.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/Dump.h> +#include <ctype.h> +#include <stdio.h> + +namespace Web::CSS { + +StyleResolver::StyleResolver(DOM::Document& document) + : m_document(document) +{ +} + +StyleResolver::~StyleResolver() +{ +} + +static StyleSheet& default_stylesheet() +{ + static StyleSheet* sheet; + if (!sheet) { + extern const char default_stylesheet_source[]; + String css = default_stylesheet_source; + sheet = parse_css(CSS::ParsingContext(), css).leak_ref(); + } + return *sheet; +} + +static StyleSheet& quirks_mode_stylesheet() +{ + static StyleSheet* sheet; + if (!sheet) { + extern const char quirks_mode_stylesheet_source[]; + String css = quirks_mode_stylesheet_source; + sheet = parse_css(CSS::ParsingContext(), css).leak_ref(); + } + return *sheet; +} + +template<typename Callback> +void StyleResolver::for_each_stylesheet(Callback callback) const +{ + callback(default_stylesheet()); + if (document().in_quirks_mode()) + callback(quirks_mode_stylesheet()); + for (auto& sheet : document().style_sheets().sheets()) { + callback(sheet); + } +} + +Vector<MatchingRule> StyleResolver::collect_matching_rules(const DOM::Element& element) const +{ + Vector<MatchingRule> matching_rules; + + size_t style_sheet_index = 0; + for_each_stylesheet([&](auto& sheet) { + size_t rule_index = 0; + for (auto& rule : sheet.rules()) { + size_t selector_index = 0; + for (auto& selector : rule.selectors()) { + if (SelectorEngine::matches(selector, element)) { + matching_rules.append({ rule, style_sheet_index, rule_index, selector_index }); + break; + } + ++selector_index; + } + ++rule_index; + } + ++style_sheet_index; + }); + + return matching_rules; +} + +void StyleResolver::sort_matching_rules(Vector<MatchingRule>& matching_rules) const +{ + quick_sort(matching_rules, [&](MatchingRule& a, MatchingRule& b) { + auto& a_selector = a.rule->selectors()[a.selector_index]; + auto& b_selector = b.rule->selectors()[b.selector_index]; + auto a_specificity = a_selector.specificity(); + auto b_specificity = b_selector.specificity(); + if (a_selector.specificity() == b_selector.specificity()) { + if (a.style_sheet_index == b.style_sheet_index) + return a.rule_index < b.rule_index; + return a.style_sheet_index < b.style_sheet_index; + } + return a_specificity < b_specificity; + }); +} + +bool StyleResolver::is_inherited_property(CSS::PropertyID property_id) +{ + static HashTable<CSS::PropertyID> inherited_properties; + if (inherited_properties.is_empty()) { + inherited_properties.set(CSS::PropertyID::BorderCollapse); + inherited_properties.set(CSS::PropertyID::BorderSpacing); + inherited_properties.set(CSS::PropertyID::Color); + inherited_properties.set(CSS::PropertyID::FontFamily); + inherited_properties.set(CSS::PropertyID::FontSize); + inherited_properties.set(CSS::PropertyID::FontStyle); + inherited_properties.set(CSS::PropertyID::FontVariant); + inherited_properties.set(CSS::PropertyID::FontWeight); + inherited_properties.set(CSS::PropertyID::LetterSpacing); + inherited_properties.set(CSS::PropertyID::LineHeight); + inherited_properties.set(CSS::PropertyID::ListStyle); + inherited_properties.set(CSS::PropertyID::ListStyleImage); + inherited_properties.set(CSS::PropertyID::ListStylePosition); + inherited_properties.set(CSS::PropertyID::ListStyleType); + inherited_properties.set(CSS::PropertyID::TextAlign); + inherited_properties.set(CSS::PropertyID::TextIndent); + inherited_properties.set(CSS::PropertyID::TextTransform); + inherited_properties.set(CSS::PropertyID::Visibility); + inherited_properties.set(CSS::PropertyID::WhiteSpace); + inherited_properties.set(CSS::PropertyID::WordSpacing); + + // FIXME: This property is not supposed to be inherited, but we currently + // rely on inheritance to propagate decorations into line boxes. + inherited_properties.set(CSS::PropertyID::TextDecorationLine); + } + return inherited_properties.contains(property_id); +} + +static Vector<String> split_on_whitespace(const StringView& string) +{ + if (string.is_empty()) + return {}; + + Vector<String> v; + size_t substart = 0; + for (size_t i = 0; i < string.length(); ++i) { + char ch = string.characters_without_null_termination()[i]; + if (isspace(ch)) { + size_t sublen = i - substart; + if (sublen != 0) + v.append(string.substring_view(substart, sublen)); + substart = i + 1; + } + } + size_t taillen = string.length() - substart; + if (taillen != 0) + v.append(string.substring_view(substart, taillen)); + return v; +} + +enum class Edge { + Top, + Right, + Bottom, + Left, + All, +}; + +static bool contains(Edge a, Edge b) +{ + return a == b || b == Edge::All; +} + +static inline void set_property_border_width(StyleProperties& style, const StyleValue& value, Edge edge) +{ + ASSERT(value.is_length()); + if (contains(Edge::Top, edge)) + style.set_property(CSS::PropertyID::BorderTopWidth, value); + if (contains(Edge::Right, edge)) + style.set_property(CSS::PropertyID::BorderRightWidth, value); + if (contains(Edge::Bottom, edge)) + style.set_property(CSS::PropertyID::BorderBottomWidth, value); + if (contains(Edge::Left, edge)) + style.set_property(CSS::PropertyID::BorderLeftWidth, value); +} + +static inline void set_property_border_color(StyleProperties& style, const StyleValue& value, Edge edge) +{ + ASSERT(value.is_color()); + if (contains(Edge::Top, edge)) + style.set_property(CSS::PropertyID::BorderTopColor, value); + if (contains(Edge::Right, edge)) + style.set_property(CSS::PropertyID::BorderRightColor, value); + if (contains(Edge::Bottom, edge)) + style.set_property(CSS::PropertyID::BorderBottomColor, value); + if (contains(Edge::Left, edge)) + style.set_property(CSS::PropertyID::BorderLeftColor, value); +} + +static inline void set_property_border_style(StyleProperties& style, const StyleValue& value, Edge edge) +{ + ASSERT(value.is_string()); + if (contains(Edge::Top, edge)) + style.set_property(CSS::PropertyID::BorderTopStyle, value); + if (contains(Edge::Right, edge)) + style.set_property(CSS::PropertyID::BorderRightStyle, value); + if (contains(Edge::Bottom, edge)) + style.set_property(CSS::PropertyID::BorderBottomStyle, value); + if (contains(Edge::Left, edge)) + style.set_property(CSS::PropertyID::BorderLeftStyle, value); +} + +static void set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, const StyleValue& value, DOM::Document& document) +{ + CSS::ParsingContext context(document); + + if (property_id == CSS::PropertyID::TextDecoration) { + switch (value.to_identifier()) { + case CSS::ValueID::None: + case CSS::ValueID::Underline: + case CSS::ValueID::Overline: + case CSS::ValueID::LineThrough: + case CSS::ValueID::Blink: + set_property_expanding_shorthands(style, CSS::PropertyID::TextDecorationLine, value, document); + default: + break; + } + return; + } + + if (property_id == CSS::PropertyID::Border) { + set_property_expanding_shorthands(style, CSS::PropertyID::BorderTop, value, document); + set_property_expanding_shorthands(style, CSS::PropertyID::BorderRight, value, document); + set_property_expanding_shorthands(style, CSS::PropertyID::BorderBottom, value, document); + set_property_expanding_shorthands(style, CSS::PropertyID::BorderLeft, value, document); + return; + } + + if (property_id == CSS::PropertyID::BorderTop + || property_id == CSS::PropertyID::BorderRight + || property_id == CSS::PropertyID::BorderBottom + || property_id == CSS::PropertyID::BorderLeft) { + + Edge edge = Edge::All; + switch (property_id) { + case CSS::PropertyID::BorderTop: + edge = Edge::Top; + break; + case CSS::PropertyID::BorderRight: + edge = Edge::Right; + break; + case CSS::PropertyID::BorderBottom: + edge = Edge::Bottom; + break; + case CSS::PropertyID::BorderLeft: + edge = Edge::Left; + break; + default: + break; + } + + auto parts = split_on_whitespace(value.to_string()); + if (value.is_length()) { + set_property_border_width(style, value, edge); + return; + } + if (value.is_color()) { + set_property_border_color(style, value, edge); + return; + } + if (value.is_string()) { + auto parts = split_on_whitespace(value.to_string()); + + if (parts.size() == 1) { + if (auto value = parse_line_style(context, parts[0])) { + set_property_border_style(style, value.release_nonnull(), edge); + set_property_border_color(style, ColorStyleValue::create(Gfx::Color::Black), edge); + set_property_border_width(style, LengthStyleValue::create(Length(3, Length::Type::Px)), edge); + return; + } + } + + RefPtr<LengthStyleValue> line_width_value; + RefPtr<ColorStyleValue> color_value; + RefPtr<StringStyleValue> line_style_value; + + for (auto& part : parts) { + if (auto value = parse_line_width(context, part)) { + if (line_width_value) + return; + line_width_value = move(value); + continue; + } + if (auto value = parse_color(context, part)) { + if (color_value) + return; + color_value = move(value); + continue; + } + if (auto value = parse_line_style(context, part)) { + if (line_style_value) + return; + line_style_value = move(value); + continue; + } + } + + if (line_width_value) + set_property_border_width(style, line_width_value.release_nonnull(), edge); + if (color_value) + set_property_border_color(style, color_value.release_nonnull(), edge); + if (line_style_value) + set_property_border_style(style, line_style_value.release_nonnull(), edge); + + return; + } + return; + } + + if (property_id == CSS::PropertyID::BorderStyle) { + auto parts = split_on_whitespace(value.to_string()); + if (value.is_string() && parts.size() == 3) { + auto top = parse_css_value(context, parts[0]); + auto right = parse_css_value(context, parts[1]); + auto bottom = parse_css_value(context, parts[2]); + auto left = parse_css_value(context, parts[1]); + if (top && right && bottom && left) { + style.set_property(CSS::PropertyID::BorderTopStyle, *top); + style.set_property(CSS::PropertyID::BorderRightStyle, *right); + style.set_property(CSS::PropertyID::BorderBottomStyle, *bottom); + style.set_property(CSS::PropertyID::BorderLeftStyle, *left); + } + } else { + style.set_property(CSS::PropertyID::BorderTopStyle, value); + style.set_property(CSS::PropertyID::BorderRightStyle, value); + style.set_property(CSS::PropertyID::BorderBottomStyle, value); + style.set_property(CSS::PropertyID::BorderLeftStyle, value); + } + return; + } + + if (property_id == CSS::PropertyID::BorderWidth) { + auto parts = split_on_whitespace(value.to_string()); + if (value.is_string() && parts.size() == 2) { + auto vertical_border_width = parse_css_value(context, parts[0]); + auto horizontal_border_width = parse_css_value(context, parts[1]); + if (vertical_border_width && horizontal_border_width) { + style.set_property(CSS::PropertyID::BorderTopWidth, *vertical_border_width); + style.set_property(CSS::PropertyID::BorderRightWidth, *horizontal_border_width); + style.set_property(CSS::PropertyID::BorderBottomWidth, *vertical_border_width); + style.set_property(CSS::PropertyID::BorderLeftWidth, *horizontal_border_width); + } + } else { + style.set_property(CSS::PropertyID::BorderTopWidth, value); + style.set_property(CSS::PropertyID::BorderRightWidth, value); + style.set_property(CSS::PropertyID::BorderBottomWidth, value); + style.set_property(CSS::PropertyID::BorderLeftWidth, value); + } + return; + } + + if (property_id == CSS::PropertyID::BorderColor) { + auto parts = split_on_whitespace(value.to_string()); + if (value.is_string() && parts.size() == 4) { + auto top = parse_css_value(context, parts[0]); + auto right = parse_css_value(context, parts[1]); + auto bottom = parse_css_value(context, parts[2]); + auto left = parse_css_value(context, parts[3]); + if (top && right && bottom && left) { + style.set_property(CSS::PropertyID::BorderTopColor, *top); + style.set_property(CSS::PropertyID::BorderRightColor, *right); + style.set_property(CSS::PropertyID::BorderBottomColor, *bottom); + style.set_property(CSS::PropertyID::BorderLeftColor, *left); + } + } else { + style.set_property(CSS::PropertyID::BorderTopColor, value); + style.set_property(CSS::PropertyID::BorderRightColor, value); + style.set_property(CSS::PropertyID::BorderBottomColor, value); + style.set_property(CSS::PropertyID::BorderLeftColor, value); + } + return; + } + + if (property_id == CSS::PropertyID::Background) { + if (value.is_identifier() && static_cast<const IdentifierStyleValue&>(value).id() == CSS::ValueID::None) { + style.set_property(CSS::PropertyID::BackgroundColor, ColorStyleValue::create(Color::Transparent)); + return; + } + auto parts = split_on_whitespace(value.to_string()); + NonnullRefPtrVector<StyleValue> values; + for (auto& part : parts) { + auto value = parse_css_value(context, part); + if (!value) + return; + values.append(value.release_nonnull()); + } + + // HACK: Disallow more than one color value in a 'background' shorthand + size_t color_value_count = 0; + for (auto& value : values) + color_value_count += value.is_color(); + + if (values[0].is_color() && color_value_count == 1) + style.set_property(CSS::PropertyID::BackgroundColor, values[0]); + + for (auto& value : values) { + if (!value.is_string()) + continue; + auto string = value.to_string(); + set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundImage, value, document); + } + return; + } + + if (property_id == CSS::PropertyID::BackgroundImage) { + if (!value.is_string()) + return; + auto string = value.to_string(); + if (!string.starts_with("url(")) + return; + if (!string.ends_with(')')) + return; + auto url = string.substring_view(4, string.length() - 5); + if (url.length() >= 2 && url.starts_with('"') && url.ends_with('"')) + url = url.substring_view(1, url.length() - 2); + else if (url.length() >= 2 && url.starts_with('\'') && url.ends_with('\'')) + url = url.substring_view(1, url.length() - 2); + + auto background_image_value = ImageStyleValue::create(document.complete_url(url), document); + style.set_property(CSS::PropertyID::BackgroundImage, move(background_image_value)); + return; + } + + if (property_id == CSS::PropertyID::Margin) { + if (value.is_length()) { + style.set_property(CSS::PropertyID::MarginTop, value); + style.set_property(CSS::PropertyID::MarginRight, value); + style.set_property(CSS::PropertyID::MarginBottom, value); + style.set_property(CSS::PropertyID::MarginLeft, value); + return; + } + if (value.is_string()) { + auto parts = split_on_whitespace(value.to_string()); + if (value.is_string() && parts.size() == 2) { + auto vertical = parse_css_value(context, parts[0]); + auto horizontal = parse_css_value(context, parts[1]); + if (vertical && horizontal) { + style.set_property(CSS::PropertyID::MarginTop, *vertical); + style.set_property(CSS::PropertyID::MarginBottom, *vertical); + style.set_property(CSS::PropertyID::MarginLeft, *horizontal); + style.set_property(CSS::PropertyID::MarginRight, *horizontal); + } + return; + } + if (value.is_string() && parts.size() == 3) { + auto top = parse_css_value(context, parts[0]); + auto horizontal = parse_css_value(context, parts[1]); + auto bottom = parse_css_value(context, parts[2]); + if (top && horizontal && bottom) { + style.set_property(CSS::PropertyID::MarginTop, *top); + style.set_property(CSS::PropertyID::MarginBottom, *bottom); + style.set_property(CSS::PropertyID::MarginLeft, *horizontal); + style.set_property(CSS::PropertyID::MarginRight, *horizontal); + } + return; + } + if (value.is_string() && parts.size() == 4) { + auto top = parse_css_value(context, parts[0]); + auto right = parse_css_value(context, parts[1]); + auto bottom = parse_css_value(context, parts[2]); + auto left = parse_css_value(context, parts[3]); + if (top && right && bottom && left) { + style.set_property(CSS::PropertyID::MarginTop, *top); + style.set_property(CSS::PropertyID::MarginBottom, *bottom); + style.set_property(CSS::PropertyID::MarginLeft, *left); + style.set_property(CSS::PropertyID::MarginRight, *right); + } + return; + } + dbg() << "Unsure what to do with CSS margin value '" << value.to_string() << "'"; + return; + } + return; + } + + if (property_id == CSS::PropertyID::Padding) { + if (value.is_length()) { + style.set_property(CSS::PropertyID::PaddingTop, value); + style.set_property(CSS::PropertyID::PaddingRight, value); + style.set_property(CSS::PropertyID::PaddingBottom, value); + style.set_property(CSS::PropertyID::PaddingLeft, value); + return; + } + if (value.is_string()) { + auto parts = split_on_whitespace(value.to_string()); + if (value.is_string() && parts.size() == 2) { + auto vertical = parse_css_value(context, parts[0]); + auto horizontal = parse_css_value(context, parts[1]); + if (vertical && horizontal) { + style.set_property(CSS::PropertyID::PaddingTop, *vertical); + style.set_property(CSS::PropertyID::PaddingBottom, *vertical); + style.set_property(CSS::PropertyID::PaddingLeft, *horizontal); + style.set_property(CSS::PropertyID::PaddingRight, *horizontal); + } + return; + } + if (value.is_string() && parts.size() == 3) { + auto top = parse_css_value(context, parts[0]); + auto horizontal = parse_css_value(context, parts[1]); + auto bottom = parse_css_value(context, parts[2]); + if (top && bottom && horizontal) { + style.set_property(CSS::PropertyID::PaddingTop, *top); + style.set_property(CSS::PropertyID::PaddingBottom, *bottom); + style.set_property(CSS::PropertyID::PaddingLeft, *horizontal); + style.set_property(CSS::PropertyID::PaddingRight, *horizontal); + } + return; + } + if (value.is_string() && parts.size() == 4) { + auto top = parse_css_value(context, parts[0]); + auto right = parse_css_value(context, parts[1]); + auto bottom = parse_css_value(context, parts[2]); + auto left = parse_css_value(context, parts[3]); + if (top && bottom && left && right) { + style.set_property(CSS::PropertyID::PaddingTop, *top); + style.set_property(CSS::PropertyID::PaddingBottom, *bottom); + style.set_property(CSS::PropertyID::PaddingLeft, *left); + style.set_property(CSS::PropertyID::PaddingRight, *right); + } + return; + } + dbg() << "Unsure what to do with CSS padding value '" << value.to_string() << "'"; + return; + } + return; + } + + if (property_id == CSS::PropertyID::ListStyle) { + auto parts = split_on_whitespace(value.to_string()); + if (!parts.is_empty()) { + auto value = parse_css_value(context, parts[0]); + if (!value) + return; + style.set_property(CSS::PropertyID::ListStyleType, value.release_nonnull()); + } + return; + } + + style.set_property(property_id, value); +} + +NonnullRefPtr<StyleProperties> StyleResolver::resolve_style(const DOM::Element& element) const +{ + auto style = StyleProperties::create(); + + if (auto* parent_style = element.parent_element() ? element.parent_element()->specified_css_values() : nullptr) { + parent_style->for_each_property([&](auto property_id, auto& value) { + if (is_inherited_property(property_id)) + set_property_expanding_shorthands(style, property_id, value, m_document); + }); + } + + element.apply_presentational_hints(*style); + + auto matching_rules = collect_matching_rules(element); + sort_matching_rules(matching_rules); + + for (auto& match : matching_rules) { + for (auto& property : match.rule->declaration().properties()) { + set_property_expanding_shorthands(style, property.property_id, property.value, m_document); + } + } + + if (auto* inline_style = element.inline_style()) { + for (auto& property : inline_style->properties()) { + set_property_expanding_shorthands(style, property.property_id, property.value, m_document); + } + } + + return style; +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleResolver.h b/Userland/Libraries/LibWeb/CSS/StyleResolver.h new file mode 100644 index 0000000000..0dde35a261 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleResolver.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018-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 <AK/NonnullRefPtrVector.h> +#include <AK/OwnPtr.h> +#include <LibWeb/CSS/StyleProperties.h> +#include <LibWeb/Forward.h> + +namespace Web::CSS { + +struct MatchingRule { + RefPtr<StyleRule> rule; + size_t style_sheet_index { 0 }; + size_t rule_index { 0 }; + size_t selector_index { 0 }; +}; + +class StyleResolver { +public: + explicit StyleResolver(DOM::Document&); + ~StyleResolver(); + + DOM::Document& document() { return m_document; } + const DOM::Document& document() const { return m_document; } + + NonnullRefPtr<StyleProperties> resolve_style(const DOM::Element&) const; + + Vector<MatchingRule> collect_matching_rules(const DOM::Element&) const; + void sort_matching_rules(Vector<MatchingRule>&) const; + + static bool is_inherited_property(CSS::PropertyID); + +private: + template<typename Callback> + void for_each_stylesheet(Callback) const; + + DOM::Document& m_document; +}; + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleRule.cpp b/Userland/Libraries/LibWeb/CSS/StyleRule.cpp new file mode 100644 index 0000000000..d92f362500 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleRule.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-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 <LibWeb/CSS/StyleRule.h> + +namespace Web::CSS { + +StyleRule::StyleRule(Vector<Selector>&& selectors, NonnullRefPtr<StyleDeclaration>&& declaration) + : m_selectors(move(selectors)) + , m_declaration(move(declaration)) +{ +} + +StyleRule::~StyleRule() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleRule.h b/Userland/Libraries/LibWeb/CSS/StyleRule.h new file mode 100644 index 0000000000..d668c5b42b --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleRule.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018-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 <AK/NonnullRefPtrVector.h> +#include <LibWeb/CSS/Selector.h> +#include <LibWeb/CSS/StyleDeclaration.h> + +namespace Web::CSS { + +class StyleRule : public RefCounted<StyleRule> { + AK_MAKE_NONCOPYABLE(StyleRule); + AK_MAKE_NONMOVABLE(StyleRule); + +public: + static NonnullRefPtr<StyleRule> create(Vector<Selector>&& selectors, NonnullRefPtr<StyleDeclaration>&& declaration) + { + return adopt(*new StyleRule(move(selectors), move(declaration))); + } + + ~StyleRule(); + + const Vector<Selector>& selectors() const { return m_selectors; } + const StyleDeclaration& declaration() const { return m_declaration; } + +private: + StyleRule(Vector<Selector>&&, NonnullRefPtr<StyleDeclaration>&&); + + Vector<Selector> m_selectors; + NonnullRefPtr<StyleDeclaration> m_declaration; +}; + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleSheet.cpp b/Userland/Libraries/LibWeb/CSS/StyleSheet.cpp new file mode 100644 index 0000000000..d4c37ba53e --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleSheet.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-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 <LibWeb/CSS/StyleSheet.h> + +namespace Web::CSS { + +StyleSheet::StyleSheet(NonnullRefPtrVector<StyleRule>&& rules) + : m_rules(move(rules)) +{ +} + +StyleSheet::~StyleSheet() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleSheet.h b/Userland/Libraries/LibWeb/CSS/StyleSheet.h new file mode 100644 index 0000000000..9e3695e03d --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleSheet.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-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 <AK/NonnullRefPtrVector.h> +#include <LibWeb/CSS/StyleRule.h> + +namespace Web::CSS { + +class StyleSheet : public RefCounted<StyleSheet> { +public: + static NonnullRefPtr<StyleSheet> create(NonnullRefPtrVector<StyleRule>&& rules) + { + return adopt(*new StyleSheet(move(rules))); + } + + ~StyleSheet(); + + const NonnullRefPtrVector<StyleRule>& rules() const { return m_rules; } + NonnullRefPtrVector<StyleRule>& rules() { return m_rules; } + +private: + explicit StyleSheet(NonnullRefPtrVector<StyleRule>&&); + + NonnullRefPtrVector<StyleRule> m_rules; +}; + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleSheetList.cpp b/Userland/Libraries/LibWeb/CSS/StyleSheetList.cpp new file mode 100644 index 0000000000..addca5598d --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleSheetList.cpp @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/CSS/StyleSheetList.h> + +namespace Web::CSS { + +void StyleSheetList::add_sheet(NonnullRefPtr<StyleSheet> sheet) +{ + m_sheets.append(move(sheet)); +} + +StyleSheetList::StyleSheetList(DOM::Document& document) + : m_document(document) +{ +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleSheetList.h b/Userland/Libraries/LibWeb/CSS/StyleSheetList.h new file mode 100644 index 0000000000..636e030f39 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleSheetList.h @@ -0,0 +1,52 @@ +/* + * 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 <AK/RefCounted.h> +#include <LibWeb/CSS/StyleSheet.h> + +namespace Web::CSS { + +class StyleSheetList : public RefCounted<StyleSheetList> { +public: + static NonnullRefPtr<StyleSheetList> create(DOM::Document& document) + { + return adopt(*new StyleSheetList(document)); + } + + void add_sheet(NonnullRefPtr<StyleSheet>); + + const NonnullRefPtrVector<StyleSheet>& sheets() const { return m_sheets; } + +private: + explicit StyleSheetList(DOM::Document&); + + DOM::Document& m_document; + NonnullRefPtrVector<StyleSheet> m_sheets; +}; + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp new file mode 100644 index 0000000000..f961649bdd --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2018-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/ByteBuffer.h> +#include <LibGfx/PNGLoader.h> +#include <LibGfx/Palette.h> +#include <LibWeb/CSS/StyleValue.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/InProcessWebView.h> +#include <LibWeb/Loader/LoadRequest.h> +#include <LibWeb/Loader/ResourceLoader.h> +#include <LibWeb/Page/Frame.h> + +namespace Web::CSS { + +StyleValue::StyleValue(Type type) + : m_type(type) +{ +} + +StyleValue::~StyleValue() +{ +} + +String IdentifierStyleValue::to_string() const +{ + return CSS::string_from_value_id(m_id); +} + +Color IdentifierStyleValue::to_color(const DOM::Document& document) const +{ + if (id() == CSS::ValueID::LibwebLink) + return document.link_color(); + + ASSERT(document.page()); + auto palette = document.page()->palette(); + switch (id()) { + case CSS::ValueID::LibwebPaletteDesktopBackground: + return palette.color(ColorRole::DesktopBackground); + case CSS::ValueID::LibwebPaletteActiveWindowBorder1: + return palette.color(ColorRole::ActiveWindowBorder1); + case CSS::ValueID::LibwebPaletteActiveWindowBorder2: + return palette.color(ColorRole::ActiveWindowBorder2); + case CSS::ValueID::LibwebPaletteActiveWindowTitle: + return palette.color(ColorRole::ActiveWindowTitle); + case CSS::ValueID::LibwebPaletteInactiveWindowBorder1: + return palette.color(ColorRole::InactiveWindowBorder1); + case CSS::ValueID::LibwebPaletteInactiveWindowBorder2: + return palette.color(ColorRole::InactiveWindowBorder2); + case CSS::ValueID::LibwebPaletteInactiveWindowTitle: + return palette.color(ColorRole::InactiveWindowTitle); + case CSS::ValueID::LibwebPaletteMovingWindowBorder1: + return palette.color(ColorRole::MovingWindowBorder1); + case CSS::ValueID::LibwebPaletteMovingWindowBorder2: + return palette.color(ColorRole::MovingWindowBorder2); + case CSS::ValueID::LibwebPaletteMovingWindowTitle: + return palette.color(ColorRole::MovingWindowTitle); + case CSS::ValueID::LibwebPaletteHighlightWindowBorder1: + return palette.color(ColorRole::HighlightWindowBorder1); + case CSS::ValueID::LibwebPaletteHighlightWindowBorder2: + return palette.color(ColorRole::HighlightWindowBorder2); + case CSS::ValueID::LibwebPaletteHighlightWindowTitle: + return palette.color(ColorRole::HighlightWindowTitle); + case CSS::ValueID::LibwebPaletteMenuStripe: + return palette.color(ColorRole::MenuStripe); + case CSS::ValueID::LibwebPaletteMenuBase: + return palette.color(ColorRole::MenuBase); + case CSS::ValueID::LibwebPaletteMenuBaseText: + return palette.color(ColorRole::MenuBaseText); + case CSS::ValueID::LibwebPaletteMenuSelection: + return palette.color(ColorRole::MenuSelection); + case CSS::ValueID::LibwebPaletteMenuSelectionText: + return palette.color(ColorRole::MenuSelectionText); + case CSS::ValueID::LibwebPaletteWindow: + return palette.color(ColorRole::Window); + case CSS::ValueID::LibwebPaletteWindowText: + return palette.color(ColorRole::WindowText); + case CSS::ValueID::LibwebPaletteButton: + return palette.color(ColorRole::Button); + case CSS::ValueID::LibwebPaletteButtonText: + return palette.color(ColorRole::ButtonText); + case CSS::ValueID::LibwebPaletteBase: + return palette.color(ColorRole::Base); + case CSS::ValueID::LibwebPaletteBaseText: + return palette.color(ColorRole::BaseText); + case CSS::ValueID::LibwebPaletteThreedHighlight: + return palette.color(ColorRole::ThreedHighlight); + case CSS::ValueID::LibwebPaletteThreedShadow1: + return palette.color(ColorRole::ThreedShadow1); + case CSS::ValueID::LibwebPaletteThreedShadow2: + return palette.color(ColorRole::ThreedShadow2); + case CSS::ValueID::LibwebPaletteHoverHighlight: + return palette.color(ColorRole::HoverHighlight); + case CSS::ValueID::LibwebPaletteSelection: + return palette.color(ColorRole::Selection); + case CSS::ValueID::LibwebPaletteSelectionText: + return palette.color(ColorRole::SelectionText); + case CSS::ValueID::LibwebPaletteInactiveSelection: + return palette.color(ColorRole::InactiveSelection); + case CSS::ValueID::LibwebPaletteInactiveSelectionText: + return palette.color(ColorRole::InactiveSelectionText); + case CSS::ValueID::LibwebPaletteRubberBandFill: + return palette.color(ColorRole::RubberBandFill); + case CSS::ValueID::LibwebPaletteRubberBandBorder: + return palette.color(ColorRole::RubberBandBorder); + case CSS::ValueID::LibwebPaletteLink: + return palette.color(ColorRole::Link); + case CSS::ValueID::LibwebPaletteActiveLink: + return palette.color(ColorRole::ActiveLink); + case CSS::ValueID::LibwebPaletteVisitedLink: + return palette.color(ColorRole::VisitedLink); + case CSS::ValueID::LibwebPaletteRuler: + return palette.color(ColorRole::Ruler); + case CSS::ValueID::LibwebPaletteRulerBorder: + return palette.color(ColorRole::RulerBorder); + case CSS::ValueID::LibwebPaletteRulerActiveText: + return palette.color(ColorRole::RulerActiveText); + case CSS::ValueID::LibwebPaletteRulerInactiveText: + return palette.color(ColorRole::RulerInactiveText); + case CSS::ValueID::LibwebPaletteTextCursor: + return palette.color(ColorRole::TextCursor); + case CSS::ValueID::LibwebPaletteFocusOutline: + return palette.color(ColorRole::FocusOutline); + case CSS::ValueID::LibwebPaletteSyntaxComment: + return palette.color(ColorRole::SyntaxComment); + case CSS::ValueID::LibwebPaletteSyntaxNumber: + return palette.color(ColorRole::SyntaxNumber); + case CSS::ValueID::LibwebPaletteSyntaxString: + return palette.color(ColorRole::SyntaxString); + case CSS::ValueID::LibwebPaletteSyntaxType: + return palette.color(ColorRole::SyntaxType); + case CSS::ValueID::LibwebPaletteSyntaxPunctuation: + return palette.color(ColorRole::SyntaxPunctuation); + case CSS::ValueID::LibwebPaletteSyntaxOperator: + return palette.color(ColorRole::SyntaxOperator); + case CSS::ValueID::LibwebPaletteSyntaxKeyword: + return palette.color(ColorRole::SyntaxKeyword); + case CSS::ValueID::LibwebPaletteSyntaxControlKeyword: + return palette.color(ColorRole::SyntaxControlKeyword); + case CSS::ValueID::LibwebPaletteSyntaxIdentifier: + return palette.color(ColorRole::SyntaxIdentifier); + case CSS::ValueID::LibwebPaletteSyntaxPreprocessorStatement: + return palette.color(ColorRole::SyntaxPreprocessorStatement); + case CSS::ValueID::LibwebPaletteSyntaxPreprocessorValue: + return palette.color(ColorRole::SyntaxPreprocessorValue); + default: + return {}; + } +} + +ImageStyleValue::ImageStyleValue(const URL& url, DOM::Document& document) + : StyleValue(Type::Image) + , m_url(url) + , m_document(document) +{ + LoadRequest request; + request.set_url(url); + set_resource(ResourceLoader::the().load_resource(Resource::Type::Image, request)); +} + +void ImageStyleValue::resource_did_load() +{ + if (!m_document) + return; + m_bitmap = resource()->bitmap(); + // FIXME: Do less than a full repaint if possible? + if (m_document->frame()) + m_document->frame()->set_needs_display({}); +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h new file mode 100644 index 0000000000..4b3d1d3ecc --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2018-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 <AK/RefCounted.h> +#include <AK/RefPtr.h> +#include <AK/String.h> +#include <AK/StringView.h> +#include <AK/URL.h> +#include <AK/WeakPtr.h> +#include <LibGfx/Bitmap.h> +#include <LibGfx/Color.h> +#include <LibWeb/CSS/Length.h> +#include <LibWeb/CSS/PropertyID.h> +#include <LibWeb/CSS/ValueID.h> +#include <LibWeb/Forward.h> +#include <LibWeb/Loader/ImageResource.h> + +namespace Web::CSS { + +enum class Position { + Static, + Relative, + Absolute, + Fixed, + Sticky, +}; + +enum class TextAlign { + Left, + Center, + Right, + Justify, + LibwebCenter, +}; + +enum class TextDecorationLine { + None, + Underline, + Overline, + LineThrough, + Blink, +}; + +enum class TextTransform { + None, + Capitalize, + Uppercase, + Lowercase, + FullWidth, + FullSizeKana, +}; + +enum class Display { + None, + Block, + Inline, + InlineBlock, + ListItem, + Table, + TableRow, + TableCell, + TableHeaderGroup, + TableRowGroup, + TableFooterGroup, + TableColumn, + TableColumnGroup, + TableCaption, +}; + +enum class WhiteSpace { + Normal, + Pre, + Nowrap, + PreLine, + PreWrap, +}; + +enum class Float { + None, + Left, + Right, +}; + +enum class Clear { + None, + Left, + Right, + Both, +}; + +enum class LineStyle { + None, + Hidden, + Dotted, + Dashed, + Solid, + Double, + Groove, + Ridge, + Inset, + Outset, +}; + +enum class ListStyleType { + None, + Disc, + Circle, + Square, + Decimal, +}; + +class StyleValue : public RefCounted<StyleValue> { +public: + virtual ~StyleValue(); + + enum class Type { + Invalid, + Inherit, + Initial, + String, + Length, + Color, + Identifier, + Image, + Position, + }; + + Type type() const { return m_type; } + + bool is_inherit() const { return type() == Type::Inherit; } + bool is_initial() const { return type() == Type::Initial; } + bool is_color() const { return type() == Type::Color; } + bool is_identifier() const { return type() == Type::Identifier; } + bool is_image() const { return type() == Type::Image; } + bool is_string() const { return type() == Type::String; } + bool is_length() const { return type() == Type::Length; } + bool is_position() const { return type() == Type::Position; } + + virtual String to_string() const = 0; + virtual Length to_length() const { return Length::make_auto(); } + virtual Color to_color(const DOM::Document&) const { return {}; } + + CSS::ValueID to_identifier() const; + + virtual bool is_auto() const { return false; } + + bool operator==(const StyleValue& other) const { return equals(other); } + bool operator!=(const StyleValue& other) const { return !(*this == other); } + + virtual bool equals(const StyleValue& other) const + { + if (type() != other.type()) + return false; + return to_string() == other.to_string(); + } + +protected: + explicit StyleValue(Type); + +private: + Type m_type { Type::Invalid }; +}; + +class StringStyleValue : public StyleValue { +public: + static NonnullRefPtr<StringStyleValue> create(const String& string) + { + return adopt(*new StringStyleValue(string)); + } + virtual ~StringStyleValue() override { } + + String to_string() const override { return m_string; } + +private: + explicit StringStyleValue(const String& string) + : StyleValue(Type::String) + , m_string(string) + { + } + + String m_string; +}; + +class LengthStyleValue : public StyleValue { +public: + static NonnullRefPtr<LengthStyleValue> create(const Length& length) + { + return adopt(*new LengthStyleValue(length)); + } + virtual ~LengthStyleValue() override { } + + virtual String to_string() const override { return m_length.to_string(); } + virtual Length to_length() const override { return m_length; } + + const Length& length() const { return m_length; } + + virtual bool is_auto() const override { return m_length.is_auto(); } + + virtual bool equals(const StyleValue& other) const override + { + if (type() != other.type()) + return false; + return m_length == static_cast<const LengthStyleValue&>(other).m_length; + } + +private: + explicit LengthStyleValue(const Length& length) + : StyleValue(Type::Length) + , m_length(length) + { + } + + Length m_length; +}; + +class InitialStyleValue final : public StyleValue { +public: + static NonnullRefPtr<InitialStyleValue> create() { return adopt(*new InitialStyleValue); } + virtual ~InitialStyleValue() override { } + + String to_string() const override { return "initial"; } + +private: + InitialStyleValue() + : StyleValue(Type::Initial) + { + } +}; + +class InheritStyleValue final : public StyleValue { +public: + static NonnullRefPtr<InheritStyleValue> create() { return adopt(*new InheritStyleValue); } + virtual ~InheritStyleValue() override { } + + String to_string() const override { return "inherit"; } + +private: + InheritStyleValue() + : StyleValue(Type::Inherit) + { + } +}; + +class ColorStyleValue : public StyleValue { +public: + static NonnullRefPtr<ColorStyleValue> create(Color color) + { + return adopt(*new ColorStyleValue(color)); + } + virtual ~ColorStyleValue() override { } + + Color color() const { return m_color; } + String to_string() const override { return m_color.to_string(); } + Color to_color(const DOM::Document&) const override { return m_color; } + + virtual bool equals(const StyleValue& other) const override + { + if (type() != other.type()) + return false; + return m_color == static_cast<const ColorStyleValue&>(other).m_color; + } + +private: + explicit ColorStyleValue(Color color) + : StyleValue(Type::Color) + , m_color(color) + { + } + + Color m_color; +}; + +class IdentifierStyleValue final : public StyleValue { +public: + static NonnullRefPtr<IdentifierStyleValue> create(CSS::ValueID id) + { + return adopt(*new IdentifierStyleValue(id)); + } + virtual ~IdentifierStyleValue() override { } + + CSS::ValueID id() const { return m_id; } + + virtual String to_string() const override; + virtual Color to_color(const DOM::Document&) const override; + + virtual bool equals(const StyleValue& other) const override + { + if (type() != other.type()) + return false; + return m_id == static_cast<const IdentifierStyleValue&>(other).m_id; + } + +private: + explicit IdentifierStyleValue(CSS::ValueID id) + : StyleValue(Type::Identifier) + , m_id(id) + { + } + + CSS::ValueID m_id { CSS::ValueID::Invalid }; +}; + +class ImageStyleValue final + : public StyleValue + , public ImageResourceClient { +public: + static NonnullRefPtr<ImageStyleValue> create(const URL& url, DOM::Document& document) { return adopt(*new ImageStyleValue(url, document)); } + virtual ~ImageStyleValue() override { } + + String to_string() const override { return String::formatted("Image({})", m_url.to_string()); } + + const Gfx::Bitmap* bitmap() const { return m_bitmap; } + +private: + ImageStyleValue(const URL&, DOM::Document&); + + // ^ResourceClient + virtual void resource_did_load() override; + + URL m_url; + WeakPtr<DOM::Document> m_document; + RefPtr<Gfx::Bitmap> m_bitmap; +}; + +inline CSS::ValueID StyleValue::to_identifier() const +{ + if (is_identifier()) + return static_cast<const IdentifierStyleValue&>(*this).id(); + return CSS::ValueID::Invalid; +} + +} diff --git a/Userland/Libraries/LibWeb/CodeGenerators/CMakeLists.txt b/Userland/Libraries/LibWeb/CodeGenerators/CMakeLists.txt new file mode 100644 index 0000000000..b3c4073df9 --- /dev/null +++ b/Userland/Libraries/LibWeb/CodeGenerators/CMakeLists.txt @@ -0,0 +1,11 @@ +add_executable(Generate_CSS_PropertyID_h Generate_CSS_PropertyID_h.cpp) +add_executable(Generate_CSS_PropertyID_cpp Generate_CSS_PropertyID_cpp.cpp) +add_executable(Generate_CSS_ValueID_h Generate_CSS_ValueID_h.cpp) +add_executable(Generate_CSS_ValueID_cpp Generate_CSS_ValueID_cpp.cpp) +add_executable(WrapperGenerator WrapperGenerator.cpp) +target_compile_options(WrapperGenerator PUBLIC -g) +target_link_libraries(Generate_CSS_PropertyID_h LagomCore) +target_link_libraries(Generate_CSS_PropertyID_cpp LagomCore) +target_link_libraries(Generate_CSS_ValueID_h LagomCore) +target_link_libraries(Generate_CSS_ValueID_cpp LagomCore) +target_link_libraries(WrapperGenerator LagomCore) diff --git a/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_PropertyID_cpp.cpp b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_PropertyID_cpp.cpp new file mode 100644 index 0000000000..2b7b2218c7 --- /dev/null +++ b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_PropertyID_cpp.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018-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/ByteBuffer.h> +#include <AK/JsonObject.h> +#include <AK/SourceGenerator.h> +#include <AK/StringBuilder.h> +#include <LibCore/File.h> +#include <ctype.h> +#include <stdio.h> + +static String title_casify(const String& dashy_name) +{ + auto parts = dashy_name.split('-'); + StringBuilder builder; + for (auto& part : parts) { + if (part.is_empty()) + continue; + builder.append(toupper(part[0])); + if (part.length() == 1) + continue; + builder.append(part.substring_view(1, part.length() - 1)); + } + return builder.to_string(); +} + +int main(int argc, char** argv) +{ + if (argc != 2) { + warnln("usage: {} <path/to/CSS/Properties.json>", argv[0]); + return 1; + } + auto file = Core::File::construct(argv[1]); + if (!file->open(Core::IODevice::ReadOnly)) + return 1; + + auto json = JsonValue::from_string(file->read_all()); + ASSERT(json.has_value()); + ASSERT(json.value().is_object()); + + StringBuilder builder; + SourceGenerator generator { builder }; + + generator.append(R"~~~( +#include <AK/Assertions.h> +#include <LibWeb/CSS/PropertyID.h> + +namespace Web::CSS { + +PropertyID property_id_from_string(const StringView& string) +{ +)~~~"); + + json.value().as_object().for_each_member([&](auto& name, auto& value) { + ASSERT(value.is_object()); + + auto member_generator = generator.fork(); + member_generator.set("name", name); + member_generator.set("name:titlecase", title_casify(name)); + member_generator.append(R"~~~( + if (string.equals_ignoring_case("@name@")) + return PropertyID::@name:titlecase@; +)~~~"); + }); + + generator.append(R"~~~( + return PropertyID::Invalid; +} + +const char* string_from_property_id(PropertyID property_id) { + switch (property_id) { +)~~~"); + + json.value().as_object().for_each_member([&](auto& name, auto& value) { + ASSERT(value.is_object()); + + auto member_generator = generator.fork(); + member_generator.set("name", name); + member_generator.set("name:titlecase", title_casify(name)); + member_generator.append(R"~~~( + case PropertyID::@name:titlecase@: + return "@name@"; + )~~~"); + }); + + generator.append(R"~~~( + default: + return "(invalid CSS::PropertyID)"; + } +} + +} // namespace Web::CSS +)~~~"); + + outln("{}", generator.as_string_view()); +} diff --git a/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_PropertyID_h.cpp b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_PropertyID_h.cpp new file mode 100644 index 0000000000..e2f235db62 --- /dev/null +++ b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_PropertyID_h.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018-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/ByteBuffer.h> +#include <AK/JsonObject.h> +#include <AK/SourceGenerator.h> +#include <AK/StringBuilder.h> +#include <LibCore/File.h> +#include <ctype.h> +#include <stdio.h> + +static String title_casify(const String& dashy_name) +{ + auto parts = dashy_name.split('-'); + StringBuilder builder; + for (auto& part : parts) { + if (part.is_empty()) + continue; + builder.append(toupper(part[0])); + if (part.length() == 1) + continue; + builder.append(part.substring_view(1, part.length() - 1)); + } + return builder.to_string(); +} + +int main(int argc, char** argv) +{ + if (argc != 2) { + warnln("usage: {} <path/to/CSS/Properties.json>", argv[0]); + return 1; + } + auto file = Core::File::construct(argv[1]); + if (!file->open(Core::IODevice::ReadOnly)) + return 1; + + auto json = JsonValue::from_string(file->read_all()); + ASSERT(json.has_value()); + ASSERT(json.value().is_object()); + + StringBuilder builder; + SourceGenerator generator { builder }; + generator.append(R"~~~( +#pragma once + +#include <AK/StringView.h> +#include <AK/Traits.h> + +namespace Web::CSS { + +enum class PropertyID { + Invalid, +)~~~"); + + json.value().as_object().for_each_member([&](auto& name, auto& value) { + ASSERT(value.is_object()); + + auto member_generator = generator.fork(); + member_generator.set("name:titlecase", title_casify(name)); + + member_generator.append(R"~~~( + @name:titlecase@, +)~~~"); + }); + + generator.append(R"~~~( +}; + +PropertyID property_id_from_string(const StringView&); +const char* string_from_property_id(PropertyID); + +} // namespace Web::CSS + +namespace AK { +template<> +struct Traits<Web::CSS::PropertyID> : public GenericTraits<Web::CSS::PropertyID> { + static unsigned hash(Web::CSS::PropertyID property_id) { return int_hash((unsigned)property_id); } +}; +} // namespace AK +)~~~"); + + outln("{}", generator.as_string_view()); +} diff --git a/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_ValueID_cpp.cpp b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_ValueID_cpp.cpp new file mode 100644 index 0000000000..4991a4040a --- /dev/null +++ b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_ValueID_cpp.cpp @@ -0,0 +1,115 @@ +/* + * 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/ByteBuffer.h> +#include <AK/JsonObject.h> +#include <AK/SourceGenerator.h> +#include <AK/StringBuilder.h> +#include <LibCore/File.h> +#include <ctype.h> +#include <stdio.h> + +static String title_casify(const String& dashy_name) +{ + auto parts = dashy_name.split('-'); + StringBuilder builder; + for (auto& part : parts) { + if (part.is_empty()) + continue; + builder.append(toupper(part[0])); + if (part.length() == 1) + continue; + builder.append(part.substring_view(1, part.length() - 1)); + } + return builder.to_string(); +} + +int main(int argc, char** argv) +{ + if (argc != 2) { + warnln("usage: {} <path/to/CSS/Identifiers.json>", argv[0]); + return 1; + } + auto file = Core::File::construct(argv[1]); + if (!file->open(Core::IODevice::ReadOnly)) + return 1; + + auto json = JsonValue::from_string(file->read_all()); + ASSERT(json.has_value()); + ASSERT(json.value().is_array()); + + StringBuilder builder; + SourceGenerator generator { builder }; + + generator.append(R"~~~( +#include <AK/Assertions.h> +#include <LibWeb/CSS/ValueID.h> + +namespace Web::CSS { + +ValueID value_id_from_string(const StringView& string) +{ +)~~~"); + + json.value().as_array().for_each([&](auto& name) { + auto member_generator = generator.fork(); + member_generator.set("name", name.to_string()); + member_generator.set("name:titlecase", title_casify(name.to_string())); + member_generator.append(R"~~~( + if (string.equals_ignoring_case("@name@")) + return ValueID::@name:titlecase@; +)~~~"); + }); + + generator.append(R"~~~( + return ValueID::Invalid; +} + +const char* string_from_value_id(ValueID value_id) { + switch (value_id) { +)~~~"); + + json.value().as_array().for_each([&](auto& name) { + auto member_generator = generator.fork(); + member_generator.set("name", name.to_string()); + member_generator.set("name:titlecase", title_casify(name.to_string())); + member_generator.append(R"~~~( + case ValueID::@name:titlecase@: + return "@name@"; + )~~~"); + }); + + generator.append(R"~~~( + default: + return "(invalid CSS::ValueID)"; + } +} + +} // namespace Web::CSS +)~~~"); + + outln("{}", generator.as_string_view()); +} diff --git a/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_ValueID_h.cpp b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_ValueID_h.cpp new file mode 100644 index 0000000000..7c85ade83f --- /dev/null +++ b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_ValueID_h.cpp @@ -0,0 +1,98 @@ +/* + * 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/ByteBuffer.h> +#include <AK/JsonObject.h> +#include <AK/SourceGenerator.h> +#include <AK/StringBuilder.h> +#include <LibCore/File.h> +#include <ctype.h> +#include <stdio.h> + +static String title_casify(const String& dashy_name) +{ + auto parts = dashy_name.split('-'); + StringBuilder builder; + for (auto& part : parts) { + if (part.is_empty()) + continue; + builder.append(toupper(part[0])); + if (part.length() == 1) + continue; + builder.append(part.substring_view(1, part.length() - 1)); + } + return builder.to_string(); +} + +int main(int argc, char** argv) +{ + if (argc != 2) { + warnln("usage: {} <path/to/CSS/Identifiers.json>", argv[0]); + return 1; + } + auto file = Core::File::construct(argv[1]); + if (!file->open(Core::IODevice::ReadOnly)) + return 1; + + auto json = JsonValue::from_string(file->read_all()); + ASSERT(json.has_value()); + ASSERT(json.value().is_array()); + + StringBuilder builder; + SourceGenerator generator { builder }; + generator.append(R"~~~( +#pragma once + +#include <AK/StringView.h> +#include <AK/Traits.h> + +namespace Web::CSS { + +enum class ValueID { + Invalid, +)~~~"); + + json.value().as_array().for_each([&](auto& name) { + auto member_generator = generator.fork(); + member_generator.set("name:titlecase", title_casify(name.to_string())); + + member_generator.append(R"~~~( + @name:titlecase@, +)~~~"); + }); + + generator.append(R"~~~( +}; + +ValueID value_id_from_string(const StringView&); +const char* string_from_value_id(ValueID); + +} + +)~~~"); + + outln("{}", generator.as_string_view()); +} diff --git a/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp b/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp new file mode 100644 index 0000000000..dbedd118b5 --- /dev/null +++ b/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp @@ -0,0 +1,951 @@ +/* + * 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/ByteBuffer.h> +#include <AK/GenericLexer.h> +#include <AK/HashMap.h> +#include <AK/LexicalPath.h> +#include <AK/SourceGenerator.h> +#include <AK/StringBuilder.h> +#include <LibCore/ArgsParser.h> +#include <LibCore/File.h> +#include <ctype.h> + +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 && !last_was_uppercase) + builder.append('_'); + builder.append(tolower(ch)); + } else { + builder.append(ch); + } + first = false; + last_was_uppercase = isupper(ch); + } + return builder.to_string(); +} + +static String make_input_acceptable_cpp(const String& input) +{ + if (input.is_one_of("class", "template", "for", "default", "char")) { + StringBuilder builder; + builder.append(input); + builder.append('_'); + return builder.to_string(); + } + + String input_without_dashes = input; + input_without_dashes.replace("-", "_"); + + return input_without_dashes; +} + +static void report_parsing_error(StringView message, StringView filename, StringView input, size_t offset) +{ + // FIXME: Spaghetti code ahead. + + size_t lineno = 1; + size_t colno = 1; + size_t start_line = 0; + size_t line_length = 0; + for (size_t index = 0; index < input.length(); ++index) { + if (offset == index) + colno = index - start_line + 1; + + if (input[index] == '\n') { + if (index >= offset) + break; + + start_line = index + 1; + line_length = 0; + ++lineno; + } else { + ++line_length; + } + } + + StringBuilder error_message; + error_message.appendff("{}\n", input.substring_view(start_line, line_length)); + for (size_t i = 0; i < colno - 1; ++i) + error_message.append(' '); + error_message.append("\033[1;31m^\n"); + error_message.appendff("{}:{}: error: {}\033[0m\n", filename, lineno, message); + + warnln("{}", error_message.string_view()); + exit(EXIT_FAILURE); +} + +namespace IDL { + +struct Type { + String name; + bool nullable { false }; +}; + +struct Parameter { + Type type; + String name; + bool optional { false }; +}; + +struct Function { + Type return_type; + String name; + Vector<Parameter> parameters; + HashMap<String, String> extended_attributes; + + size_t length() const + { + // 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; + } +}; + +struct Attribute { + bool readonly { false }; + bool unsigned_ { false }; + Type type; + String name; + HashMap<String, String> extended_attributes; + + // Added for convenience after parsing + String getter_callback_name; + String setter_callback_name; +}; + +struct Interface { + String name; + String parent_name; + + Vector<Attribute> attributes; + Vector<Function> functions; + + // Added for convenience after parsing + String wrapper_class; + String wrapper_base_class; + String fully_qualified_name; +}; + +static OwnPtr<Interface> parse_interface(StringView filename, const StringView& input) +{ + auto interface = make<Interface>(); + + GenericLexer lexer(input); + + auto assert_specific = [&](char ch) { + if (!lexer.consume_specific(ch)) + report_parsing_error(String::formatted("expected '{}'", ch), filename, input, lexer.tell()); + }; + + auto consume_whitespace = [&] { + bool consumed = true; + while (consumed) { + consumed = lexer.consume_while([](char ch) { return isspace(ch); }).length() > 0; + + if (lexer.consume_specific("//")) { + lexer.consume_until('\n'); + consumed = true; + } + } + }; + + auto assert_string = [&](const StringView& expected) { + if (!lexer.consume_specific(expected)) + report_parsing_error(String::formatted("expected '{}'", expected), filename, input, lexer.tell()); + }; + + assert_string("interface"); + consume_whitespace(); + interface->name = lexer.consume_until([](auto ch) { return isspace(ch); }); + consume_whitespace(); + if (lexer.consume_specific(':')) { + consume_whitespace(); + interface->parent_name = lexer.consume_until([](auto ch) { return isspace(ch); }); + consume_whitespace(); + } + assert_specific('{'); + + auto parse_type = [&] { + auto name = lexer.consume_until([](auto ch) { return isspace(ch) || ch == '?'; }); + auto nullable = lexer.consume_specific('?'); + return Type { name, nullable }; + }; + + auto parse_attribute = [&](HashMap<String, String>& extended_attributes) { + bool readonly = lexer.consume_specific("readonly"); + if (readonly) + consume_whitespace(); + + if (lexer.consume_specific("attribute")) + consume_whitespace(); + + bool unsigned_ = lexer.consume_specific("unsigned"); + if (unsigned_) + consume_whitespace(); + + auto type = parse_type(); + consume_whitespace(); + auto name = lexer.consume_until([](auto ch) { return isspace(ch) || ch == ';'; }); + consume_whitespace(); + assert_specific(';'); + Attribute attribute; + attribute.readonly = readonly; + attribute.unsigned_ = unsigned_; + attribute.type = type; + attribute.name = name; + attribute.getter_callback_name = String::formatted("{}_getter", snake_name(attribute.name)); + attribute.setter_callback_name = String::formatted("{}_setter", snake_name(attribute.name)); + attribute.extended_attributes = move(extended_attributes); + interface->attributes.append(move(attribute)); + }; + + auto parse_function = [&](HashMap<String, String>& extended_attributes) { + auto return_type = parse_type(); + consume_whitespace(); + auto name = lexer.consume_until([](auto ch) { return isspace(ch) || ch == '('; }); + consume_whitespace(); + assert_specific('('); + + Vector<Parameter> parameters; + + 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), optional }); + if (lexer.consume_specific(')')) + break; + assert_specific(','); + consume_whitespace(); + } + + consume_whitespace(); + assert_specific(';'); + + interface->functions.append(Function { return_type, name, move(parameters), move(extended_attributes) }); + }; + + auto parse_extended_attributes = [&] { + HashMap<String, String> extended_attributes; + for (;;) { + consume_whitespace(); + if (lexer.consume_specific(']')) + break; + auto name = lexer.consume_until([](auto ch) { return ch == ']' || ch == '=' || ch == ','; }); + if (lexer.consume_specific('=')) { + auto value = lexer.consume_until([](auto ch) { return ch == ']' || ch == ','; }); + extended_attributes.set(name, value); + } else { + extended_attributes.set(name, {}); + } + lexer.consume_specific(','); + } + consume_whitespace(); + return extended_attributes; + }; + + for (;;) { + HashMap<String, String> extended_attributes; + + consume_whitespace(); + + if (lexer.consume_specific('}')) { + consume_whitespace(); + assert_specific(';'); + break; + } + + if (lexer.consume_specific('[')) { + extended_attributes = parse_extended_attributes(); + } + + if (lexer.next_is("readonly") || lexer.next_is("attribute")) { + parse_attribute(extended_attributes); + continue; + } + + parse_function(extended_attributes); + } + + interface->wrapper_class = String::formatted("{}Wrapper", interface->name); + interface->wrapper_base_class = String::formatted("{}Wrapper", interface->parent_name.is_empty() ? String::empty() : interface->parent_name); + + return interface; +} + +} + +static void generate_header(const IDL::Interface&); +static void generate_implementation(const IDL::Interface&); + +int main(int argc, char** argv) +{ + Core::ArgsParser args_parser; + const char* path = nullptr; + bool header_mode = false; + bool implementation_mode = false; + args_parser.add_option(header_mode, "Generate the wrapper .h file", "header", 'H'); + args_parser.add_option(implementation_mode, "Generate the wrapper .cpp file", "implementation", 'I'); + args_parser.add_positional_argument(path, "IDL file", "idl-file"); + args_parser.parse(argc, argv); + + auto file_or_error = Core::File::open(path, Core::IODevice::ReadOnly); + if (file_or_error.is_error()) { + fprintf(stderr, "Cannot open %s\n", path); + return 1; + } + + LexicalPath lexical_path(path); + auto namespace_ = lexical_path.parts().at(lexical_path.parts().size() - 2); + + auto data = file_or_error.value()->read_all(); + auto interface = IDL::parse_interface(path, data); + + if (!interface) { + warnln("Cannot parse {}", path); + return 1; + } + + if (namespace_.is_one_of("DOM", "HTML", "UIEvents", "HighResolutionTime", "SVG")) { + StringBuilder builder; + builder.append(namespace_); + builder.append("::"); + builder.append(interface->name); + interface->fully_qualified_name = builder.to_string(); + } else { + interface->fully_qualified_name = interface->name; + } + +#if 0 + dbgln("Attributes:"); + for (auto& attribute : interface->attributes) { + dbg() << " " << (attribute.readonly ? "Readonly " : "") + << attribute.type.name << (attribute.type.nullable ? "?" : "") + << " " << attribute.name; + } + + dbgln("Functions:"); + for (auto& function : interface->functions) { + dbg() << " " << function.return_type.name << (function.return_type.nullable ? "?" : "") + << " " << function.name; + for (auto& parameter : function.parameters) { + dbg() << " " << parameter.type.name << (parameter.type.nullable ? "?" : "") << " " << parameter.name; + } + } +#endif + + if (header_mode) + generate_header(*interface); + + if (implementation_mode) + generate_implementation(*interface); + + return 0; +} + +static bool should_emit_wrapper_factory(const IDL::Interface& interface) +{ + // FIXME: This is very hackish. + if (interface.name == "Event") + return false; + if (interface.name == "EventTarget") + return false; + if (interface.name == "Node") + return false; + if (interface.name == "Text") + return false; + if (interface.name == "Document") + return false; + if (interface.name == "DocumentType") + return false; + if (interface.name.ends_with("Element")) + return false; + return true; +} + +static bool is_wrappable_type(const IDL::Type& type) +{ + if (type.name == "Node") + return true; + if (type.name == "Document") + return true; + if (type.name == "Text") + return true; + if (type.name == "DocumentType") + return true; + if (type.name.ends_with("Element")) + return true; + if (type.name == "ImageData") + return true; + return false; +} + +static void generate_header(const IDL::Interface& interface) +{ + StringBuilder builder; + SourceGenerator generator { builder }; + + generator.set("name", interface.name); + generator.set("fully_qualified_name", interface.fully_qualified_name); + generator.set("wrapper_base_class", interface.wrapper_base_class); + generator.set("wrapper_class", interface.wrapper_class); + generator.set("wrapper_class:snakecase", snake_name(interface.wrapper_class)); + + generator.append(R"~~~( +#pragma once + +#include <LibWeb/Bindings/Wrapper.h> + +// FIXME: This is very strange. +#if __has_include(<LibWeb/DOM/@name@.h>) +# include <LibWeb/DOM/@name@.h> +#elif __has_include(<LibWeb/HTML/@name@.h>) +# include <LibWeb/HTML/@name@.h> +#elif __has_include(<LibWeb/UIEvents/@name@.h>) +# include <LibWeb/UIEvents/@name@.h> +#elif __has_include(<LibWeb/HighResolutionTime/@name@.h>) +# include <LibWeb/HighResolutionTime/@name@.h> +#elif __has_include(<LibWeb/SVG/@name@.h>) +# include <LibWeb/SVG/@name@.h> +#endif +)~~~"); + + if (interface.wrapper_base_class != "Wrapper") { + generator.append(R"~~~( +#include <LibWeb/Bindings/@wrapper_base_class@.h> +)~~~"); + } + + generator.append(R"~~~( +namespace Web::Bindings { + +class @wrapper_class@ : public @wrapper_base_class@ { + JS_OBJECT(@wrapper_class@, @wrapper_base_class@); +public: + @wrapper_class@(JS::GlobalObject&, @fully_qualified_name@&); + virtual void initialize(JS::GlobalObject&) override; + virtual ~@wrapper_class@() override; +)~~~"); + + if (interface.wrapper_base_class == "Wrapper") { + generator.append(R"~~~( + @fully_qualified_name@& impl() { return *m_impl; } + const @fully_qualified_name@& impl() const { return *m_impl; } +)~~~"); + } else { + generator.append(R"~~~( + @fully_qualified_name@& impl() { return static_cast<@fully_qualified_name@&>(@wrapper_base_class@::impl()); } + const @fully_qualified_name@& impl() const { return static_cast<const @fully_qualified_name@&>(@wrapper_base_class@::impl()); } +)~~~"); + } + + generator.append(R"~~~( +private: +)~~~"); + + for (auto& function : interface.functions) { + auto function_generator = generator.fork(); + function_generator.set("function.name:snakecase", snake_name(function.name)); + function_generator.append(R"~~~( + JS_DECLARE_NATIVE_FUNCTION(@function.name:snakecase@); + )~~~"); + } + + for (auto& attribute : interface.attributes) { + auto attribute_generator = generator.fork(); + attribute_generator.set("attribute.name:snakecase", snake_name(attribute.name)); + attribute_generator.append(R"~~~( + JS_DECLARE_NATIVE_GETTER(@attribute.name:snakecase@_getter); +)~~~"); + + if (!attribute.readonly) { + attribute_generator.append(R"~~~( + JS_DECLARE_NATIVE_SETTER(@attribute.name:snakecase@_setter); +)~~~"); + } + } + + if (interface.wrapper_base_class == "Wrapper") { + generator.append(R"~~~( + NonnullRefPtr<@fully_qualified_name@> m_impl; + )~~~"); + } + + generator.append(R"~~~( +}; +)~~~"); + + if (should_emit_wrapper_factory(interface)) { + generator.append(R"~~~( +@wrapper_class@* wrap(JS::GlobalObject&, @fully_qualified_name@&); +)~~~"); + } + + generator.append(R"~~~( +} // namespace Web::Bindings +)~~~"); + + outln("{}", generator.as_string_view()); +} + +void generate_implementation(const IDL::Interface& interface) +{ + StringBuilder builder; + SourceGenerator generator { builder }; + + generator.set("wrapper_class", interface.wrapper_class); + generator.set("wrapper_base_class", interface.wrapper_base_class); + generator.set("fully_qualified_name", interface.fully_qualified_name); + + generator.append(R"~~~( +#include <AK/FlyString.h> +#include <LibJS/Runtime/Array.h> +#include <LibJS/Runtime/Error.h> +#include <LibJS/Runtime/Function.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/Uint8ClampedArray.h> +#include <LibJS/Runtime/Value.h> +#include <LibWeb/Bindings/@wrapper_class@.h> +#include <LibWeb/Bindings/CanvasRenderingContext2DWrapper.h> +#include <LibWeb/Bindings/CommentWrapper.h> +#include <LibWeb/Bindings/DOMImplementationWrapper.h> +#include <LibWeb/Bindings/DocumentFragmentWrapper.h> +#include <LibWeb/Bindings/DocumentTypeWrapper.h> +#include <LibWeb/Bindings/DocumentWrapper.h> +#include <LibWeb/Bindings/EventTargetWrapperFactory.h> +#include <LibWeb/Bindings/HTMLCanvasElementWrapper.h> +#include <LibWeb/Bindings/HTMLHeadElementWrapper.h> +#include <LibWeb/Bindings/HTMLImageElementWrapper.h> +#include <LibWeb/Bindings/ImageDataWrapper.h> +#include <LibWeb/Bindings/NodeWrapperFactory.h> +#include <LibWeb/Bindings/TextWrapper.h> +#include <LibWeb/Bindings/WindowObject.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/DOM/EventListener.h> +#include <LibWeb/HTML/HTMLElement.h> +#include <LibWeb/Origin.h> + +// FIXME: This is a total hack until we can figure out the namespace for a given type somehow. +using namespace Web::DOM; +using namespace Web::HTML; + +namespace Web::Bindings { + +)~~~"); + + if (interface.wrapper_base_class == "Wrapper") { + generator.append(R"~~~( +@wrapper_class@::@wrapper_class@(JS::GlobalObject& global_object, @fully_qualified_name@& impl) +: Wrapper(*global_object.object_prototype()) +, m_impl(impl) +{ +} +)~~~"); + } else { + generator.append(R"~~~( +@wrapper_class@::@wrapper_class@(JS::GlobalObject& global_object, @fully_qualified_name@& impl) +: @wrapper_base_class@(global_object, impl) +{ +} +)~~~"); + } + + generator.append(R"~~~( +void @wrapper_class@::initialize(JS::GlobalObject& global_object) +{ + [[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable | JS::Attribute::Configurable; + + @wrapper_base_class@::initialize(global_object); +)~~~"); + + for (auto& attribute : interface.attributes) { + auto attribute_generator = generator.fork(); + attribute_generator.set("attribute.name", attribute.name); + attribute_generator.set("attribute.getter_callback", attribute.getter_callback_name); + + if (attribute.readonly) + attribute_generator.set("attribute.setter_callback", "nullptr"); + else + attribute_generator.set("attribute.setter_callback", attribute.setter_callback_name); + + attribute_generator.append(R"~~~( + define_native_property("@attribute.name@", @attribute.getter_callback@, @attribute.setter_callback@, default_attributes); +)~~~"); + } + + for (auto& function : interface.functions) { + auto function_generator = generator.fork(); + function_generator.set("function.name", function.name); + function_generator.set("function.name:snakecase", snake_name(function.name)); + function_generator.set("function.name:length", String::number(function.name.length())); + + function_generator.append(R"~~~( + define_native_function("@function.name@", @function.name:snakecase@, @function.name:length@, default_attributes); +)~~~"); + } + + generator.append(R"~~~( +} + +@wrapper_class@::~@wrapper_class@() +{ +} +)~~~"); + + if (!interface.attributes.is_empty() || !interface.functions.is_empty()) { + generator.append(R"~~~( +static @fully_qualified_name@* impl_from(JS::VM& vm, JS::GlobalObject& global_object) +{ + auto* this_object = vm.this_value(global_object).to_object(global_object); + if (!this_object) + return {}; + if (!is<@wrapper_class@>(this_object)) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "@fully_qualified_name@"); + return nullptr; + } + + return &static_cast<@wrapper_class@*>(this_object)->impl(); + } +)~~~"); + } + + 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); + scoped_generator.set("js_suffix", js_suffix); + scoped_generator.set("legacy_null_to_empty_string", legacy_null_to_empty_string ? "true" : "false"); + scoped_generator.set("parameter.type.name", parameter.type.name); + + if (return_void) + scoped_generator.set("return_statement", "return;"); + else + scoped_generator.set("return_statement", "return {};"); + + // FIXME: Add support for optional to all types + if (parameter.type.name == "DOMString") { + 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()) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Function"); + @return_statement@ + } + auto @cpp_name@ = adopt(*new EventListener(JS::make_handle(&@js_name@@js_suffix@.as_function()))); +)~~~"); + } else if (is_wrappable_type(parameter.type)) { + scoped_generator.append(R"~~~( + auto @cpp_name@_object = @js_name@@js_suffix@.to_object(global_object); + if (vm.exception()) + @return_statement@ + + if (!is<@parameter.type.name@Wrapper>(@cpp_name@_object)) { + vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "@parameter.type.name@"); + @return_statement@ + } + + auto& @cpp_name@ = static_cast<@parameter.type.name@Wrapper*>(@cpp_name@_object)->impl(); +)~~~"); + } else if (parameter.type.name == "double") { + scoped_generator.append(R"~~~( + auto @cpp_name@ = @js_name@@js_suffix@.to_double(global_object); + if (vm.exception()) + @return_statement@ +)~~~"); + } else if (parameter.type.name == "boolean") { + scoped_generator.append(R"~~~( + auto @cpp_name@ = @js_name@@js_suffix@.to_boolean(); +)~~~"); + } else { + dbgln("Unimplemented JS-to-C++ conversion: {}", parameter.type.name); + ASSERT_NOT_REACHED(); + } + }; + + auto generate_arguments = [&](auto& parameters, auto& arguments_builder, bool return_void = false) { + auto arguments_generator = generator.fork(); + + Vector<String> parameter_names; + size_t argument_index = 0; + for (auto& parameter : parameters) { + parameter_names.append(snake_name(parameter.name)); + arguments_generator.set("argument.index", String::number(argument_index)); + + arguments_generator.append(R"~~~( + auto arg@argument.index@ = vm.argument(@argument.index@); +)~~~"); + // 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; + } + + arguments_builder.join(", ", parameter_names); + }; + + auto generate_return_statement = [&](auto& return_type) { + auto scoped_generator = generator.fork(); + scoped_generator.set("return_type", return_type.name); + + if (return_type.name == "undefined") { + scoped_generator.append(R"~~~( + return JS::js_undefined(); +)~~~"); + return; + } + + if (return_type.nullable) { + if (return_type.name == "DOMString") { + scoped_generator.append(R"~~~( + if (retval.is_null()) + return JS::js_null(); +)~~~"); + } else { + scoped_generator.append(R"~~~( + if (!retval) + return JS::js_null(); +)~~~"); + } + } + + if (return_type.name == "DOMString") { + scoped_generator.append(R"~~~( + return JS::js_string(vm, retval); +)~~~"); + } else if (return_type.name == "ArrayFromVector") { + // FIXME: Remove this fake type hack once it's no longer needed. + // Basically once we have NodeList we can throw this out. + scoped_generator.append(R"~~~( + auto* new_array = JS::Array::create(global_object); + for (auto& element : retval) + new_array->indexed_properties().append(wrap(global_object, element)); + + return new_array; +)~~~"); + } else if (return_type.name == "long" || return_type.name == "double" || return_type.name == "boolean" || return_type.name == "short") { + scoped_generator.append(R"~~~( + return JS::Value(retval); +)~~~"); + } else if (return_type.name == "Uint8ClampedArray") { + scoped_generator.append(R"~~~( + return retval; +)~~~"); + } else { + scoped_generator.append(R"~~~( + return wrap(global_object, const_cast<@return_type@&>(*retval)); +)~~~"); + } + }; + + for (auto& attribute : interface.attributes) { + auto attribute_generator = generator.fork(); + attribute_generator.set("attribute.getter_callback", attribute.getter_callback_name); + attribute_generator.set("attribute.setter_callback", attribute.setter_callback_name); + attribute_generator.set("attribute.name:snakecase", snake_name(attribute.name)); + + if (attribute.extended_attributes.contains("Reflect")) { + auto attribute_name = attribute.extended_attributes.get("Reflect").value(); + if (attribute_name.is_null()) + attribute_name = attribute.name; + attribute_name = make_input_acceptable_cpp(attribute_name); + + attribute_generator.set("attribute.reflect_name", attribute_name); + } else { + attribute_generator.set("attribute.reflect_name", snake_name(attribute.name)); + } + + attribute_generator.append(R"~~~( +JS_DEFINE_NATIVE_GETTER(@wrapper_class@::@attribute.getter_callback@) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; +)~~~"); + + if (attribute.extended_attributes.contains("ReturnNullIfCrossOrigin")) { + attribute_generator.append(R"~~~( + if (!impl->may_access_from_origin(static_cast<WindowObject&>(global_object).origin())) + return JS::js_null(); +)~~~"); + } + + if (attribute.extended_attributes.contains("Reflect")) { + if (attribute.type.name != "boolean") { + attribute_generator.append(R"~~~( + auto retval = impl->attribute(HTML::AttributeNames::@attribute.reflect_name@); +)~~~"); + } else { + attribute_generator.append(R"~~~( + auto retval = impl->has_attribute(HTML::AttributeNames::@attribute.reflect_name@); +)~~~"); + } + } else { + attribute_generator.append(R"~~~( + auto retval = impl->@attribute.name:snakecase@(); +)~~~"); + } + + generate_return_statement(attribute.type); + + attribute_generator.append(R"~~~( +} +)~~~"); + + if (!attribute.readonly) { + attribute_generator.append(R"~~~( +JS_DEFINE_NATIVE_SETTER(@wrapper_class@::@attribute.setter_callback@) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return; +)~~~"); + + generate_to_cpp(attribute, "value", "", "cpp_value", true, attribute.extended_attributes.contains("LegacyNullToEmptyString")); + + if (attribute.extended_attributes.contains("Reflect")) { + if (attribute.type.name != "boolean") { + attribute_generator.append(R"~~~( + impl->set_attribute(HTML::AttributeNames::@attribute.reflect_name@, cpp_value); +)~~~"); + } else { + attribute_generator.append(R"~~~( + if (!cpp_value) + impl->remove_attribute(HTML::AttributeNames::@attribute.reflect_name@); + else + impl->set_attribute(HTML::AttributeNames::@attribute.reflect_name@, String::empty()); +)~~~"); + } + } else { + attribute_generator.append(R"~~~( + impl->set_@attribute.name:snakecase@(cpp_value); +)~~~"); + } + + attribute_generator.append(R"~~~( +} +)~~~"); + } + } + + // Implementation: Functions + for (auto& function : interface.functions) { + auto function_generator = generator.fork(); + function_generator.set("function.name", function.name); + function_generator.set("function.name:snakecase", snake_name(function.name)); + function_generator.set("function.nargs", String::number(function.length())); + + function_generator.append(R"~~~(\ +JS_DEFINE_NATIVE_FUNCTION(@wrapper_class@::@function.name:snakecase@) +{ + auto* impl = impl_from(vm, global_object); + if (!impl) + return {}; +)~~~"); + + if (function.length() > 0) { + if (function.length() == 1) { + function_generator.set(".bad_arg_count", "JS::ErrorType::BadArgCountOne"); + function_generator.set(".arg_count_suffix", ""); + } else { + function_generator.set(".bad_arg_count", "JS::ErrorType::BadArgCountMany"); + function_generator.set(".arg_count_suffix", String::formatted(", \"{}\"", function.length())); + } + + function_generator.append(R"~~~( + if (vm.argument_count() < @function.nargs@) { + vm.throw_exception<JS::TypeError>(global_object, @.bad_arg_count@, "@function.name@"@.arg_count_suffix@); + return {}; + } +)~~~"); + } + + StringBuilder arguments_builder; + generate_arguments(function.parameters, arguments_builder); + + function_generator.set(".arguments", arguments_builder.string_view()); + + if (function.return_type.name != "undefined") { + function_generator.append(R"~~~( + auto retval = impl->@function.name:snakecase@(@.arguments@); +)~~~"); + } else { + function_generator.append(R"~~~( + impl->@function.name:snakecase@(@.arguments@); +)~~~"); + } + + generate_return_statement(function.return_type); + + function_generator.append(R"~~~( +} +)~~~"); + } + + if (should_emit_wrapper_factory(interface)) { + generator.append(R"~~~( +@wrapper_class@* wrap(JS::GlobalObject& global_object, @fully_qualified_name@& impl) +{ + return static_cast<@wrapper_class@*>(wrap_impl(global_object, impl)); +} +)~~~"); + } + + generator.append(R"~~~( +} // namespace Web::Bindings +)~~~"); + + outln("{}", generator.as_string_view()); +} diff --git a/Userland/Libraries/LibWeb/DOM/Attribute.h b/Userland/Libraries/LibWeb/DOM/Attribute.h new file mode 100644 index 0000000000..32ca3d11b4 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Attribute.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018-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 <AK/FlyString.h> + +namespace Web { + +class Attribute { +public: + Attribute(const FlyString& name, const String& value) + : m_name(name) + , m_value(value) + { + } + + const FlyString& name() const { return m_name; } + const String& value() const { return m_value; } + + void set_value(const String& value) { m_value = value; } + +private: + FlyString m_name; + String m_value; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/CharacterData.cpp b/Userland/Libraries/LibWeb/DOM/CharacterData.cpp new file mode 100644 index 0000000000..60fb60ef7f --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/CharacterData.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-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 <LibWeb/DOM/CharacterData.h> + +namespace Web::DOM { + +CharacterData::CharacterData(Document& document, NodeType type, const String& data) + : Node(document, type) + , m_data(data) +{ +} + +CharacterData::~CharacterData() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/CharacterData.h b/Userland/Libraries/LibWeb/DOM/CharacterData.h new file mode 100644 index 0000000000..de09340e41 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/CharacterData.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018-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 <AK/String.h> +#include <LibWeb/DOM/Node.h> +#include <LibWeb/DOM/NonDocumentTypeChildNode.h> + +namespace Web::DOM { + +class CharacterData + : public Node + , public NonDocumentTypeChildNode<CharacterData> { +public: + using WrapperType = Bindings::CharacterDataWrapper; + + virtual ~CharacterData() override; + + const String& data() const { return m_data; } + void set_data(const String& data) { m_data = data; } + + unsigned length() const { return m_data.length(); } + + virtual String text_content() const override { return m_data; } + +protected: + explicit CharacterData(Document&, NodeType, const String&); + +private: + String m_data; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/CharacterData.idl b/Userland/Libraries/LibWeb/DOM/CharacterData.idl new file mode 100644 index 0000000000..e024979c05 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/CharacterData.idl @@ -0,0 +1,9 @@ +interface CharacterData : Node { + + attribute DOMString data; + readonly attribute unsigned long length; + + readonly attribute Element? nextElementSibling; + readonly attribute Element? previousElementSibling; + +}; diff --git a/Userland/Libraries/LibWeb/DOM/Comment.cpp b/Userland/Libraries/LibWeb/DOM/Comment.cpp new file mode 100644 index 0000000000..7e3f0ab8d2 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Comment.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-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 <LibWeb/DOM/Comment.h> +#include <LibWeb/Layout/TextNode.h> + +namespace Web::DOM { + +Comment::Comment(Document& document, const String& data) + : CharacterData(document, NodeType::COMMENT_NODE, data) +{ +} + +Comment::~Comment() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/Comment.h b/Userland/Libraries/LibWeb/DOM/Comment.h new file mode 100644 index 0000000000..4ab3bfbedb --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Comment.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-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 <AK/FlyString.h> +#include <LibWeb/DOM/CharacterData.h> + +namespace Web::DOM { + +class Comment final : public CharacterData { +public: + using WrapperType = Bindings::CommentWrapper; + + explicit Comment(Document&, const String&); + virtual ~Comment() override; + + virtual FlyString node_name() const override { return "#comment"; } +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/Comment.idl b/Userland/Libraries/LibWeb/DOM/Comment.idl new file mode 100644 index 0000000000..7fb01d1d70 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Comment.idl @@ -0,0 +1,3 @@ +interface Comment : CharacterData { + +}; diff --git a/Userland/Libraries/LibWeb/DOM/DOMImplementation.cpp b/Userland/Libraries/LibWeb/DOM/DOMImplementation.cpp new file mode 100644 index 0000000000..0fdeb062b6 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/DOMImplementation.cpp @@ -0,0 +1,75 @@ +/* + * 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 <LibWeb/DOM/DOMImplementation.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/DocumentType.h> +#include <LibWeb/DOM/ElementFactory.h> +#include <LibWeb/DOM/Text.h> +#include <LibWeb/Namespace.h> +#include <LibWeb/Origin.h> + +namespace Web::DOM { + +DOMImplementation::DOMImplementation(Document& document) + : m_document(document) +{ +} + +const NonnullRefPtr<Document> DOMImplementation::create_htmldocument(const String& title) const +{ + auto html_document = Document::create(); + + html_document->set_content_type("text/html"); + html_document->set_ready_for_post_load_tasks(true); + + 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/Userland/Libraries/LibWeb/DOM/DOMImplementation.h b/Userland/Libraries/LibWeb/DOM/DOMImplementation.h new file mode 100644 index 0000000000..ac59fab4d0 --- /dev/null +++ b/Userland/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 <AK/NonnullRefPtr.h> +#include <AK/RefCounted.h> +#include <AK/Weakable.h> +#include <LibWeb/Bindings/Wrappable.h> + +namespace Web::DOM { + +class DOMImplementation final + : public RefCounted<DOMImplementation> + , public Weakable<DOMImplementation> + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::DOMImplementationWrapper; + + static NonnullRefPtr<DOMImplementation> create(Document& document) + { + return adopt(*new DOMImplementation(document)); + } + + // FIXME: snake_case in WrapperGenerator turns "createHTMLDocument" into "create_htmldocument" + const NonnullRefPtr<Document> 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/Userland/Libraries/LibWeb/DOM/DOMImplementation.idl b/Userland/Libraries/LibWeb/DOM/DOMImplementation.idl new file mode 100644 index 0000000000..c979a587c3 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/DOMImplementation.idl @@ -0,0 +1,7 @@ +interface DOMImplementation { + + Document createHTMLDocument(optional DOMString title); + + boolean hasFeature(); + +}; diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp new file mode 100644 index 0000000000..f61e81fb70 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -0,0 +1,690 @@ +/* + * Copyright (c) 2018-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/StringBuilder.h> +#include <AK/Utf8View.h> +#include <LibCore/Timer.h> +#include <LibGUI/Application.h> +#include <LibGUI/DisplayLink.h> +#include <LibGUI/MessageBox.h> +#include <LibJS/Interpreter.h> +#include <LibJS/Parser.h> +#include <LibJS/Runtime/Function.h> +#include <LibWeb/Bindings/DocumentWrapper.h> +#include <LibWeb/Bindings/WindowObject.h> +#include <LibWeb/CSS/StyleResolver.h> +#include <LibWeb/DOM/Comment.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/DocumentFragment.h> +#include <LibWeb/DOM/DocumentType.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/DOM/ElementFactory.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/DOM/Text.h> +#include <LibWeb/DOM/Window.h> +#include <LibWeb/Dump.h> +#include <LibWeb/HTML/AttributeNames.h> +#include <LibWeb/HTML/EventNames.h> +#include <LibWeb/HTML/HTMLBodyElement.h> +#include <LibWeb/HTML/HTMLFrameSetElement.h> +#include <LibWeb/HTML/HTMLHeadElement.h> +#include <LibWeb/HTML/HTMLHtmlElement.h> +#include <LibWeb/HTML/HTMLScriptElement.h> +#include <LibWeb/HTML/HTMLTitleElement.h> +#include <LibWeb/InProcessWebView.h> +#include <LibWeb/Layout/BlockFormattingContext.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> +#include <LibWeb/Layout/TreeBuilder.h> +#include <LibWeb/Namespace.h> +#include <LibWeb/Origin.h> +#include <LibWeb/Page/Frame.h> +#include <LibWeb/SVG/TagNames.h> +#include <ctype.h> +#include <stdio.h> + +namespace Web::DOM { + +Document::Document(const URL& url) + : ParentNode(*this, NodeType::DOCUMENT_NODE) + , m_style_resolver(make<CSS::StyleResolver>(*this)) + , 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(); + }); + + m_forced_layout_timer = Core::Timer::create_single_shot(0, [this] { + force_layout(); + }); +} + +Document::~Document() +{ +} + +void Document::removed_last_ref() +{ + ASSERT(!ref_count()); + ASSERT(!m_deletion_has_begun); + + if (m_referencing_node_count) { + // The document has reached ref_count==0 but still has nodes keeping it alive. + // At this point, sever all the node links we control. + // If nodes remain elsewhere (e.g JS wrappers), they will keep the document alive. + + // NOTE: This makes sure we stay alive across for the duration of the cleanup below. + increment_referencing_node_count(); + + m_focused_element = nullptr; + m_hovered_node = nullptr; + m_pending_parsing_blocking_script = nullptr; + m_inspected_node = nullptr; + m_scripts_to_execute_when_parsing_has_finished.clear(); + m_scripts_to_execute_as_soon_as_possible.clear(); + m_associated_inert_template_document = nullptr; + + m_interpreter = nullptr; + + { + // Gather up all the descendants of this document and prune them from the tree. + // FIXME: This could definitely be more elegant. + NonnullRefPtrVector<Node> descendants; + for_each_in_subtree([&](auto& node) { + if (&node != this) + descendants.append(node); + return IterationDecision::Continue; + }); + + for (auto& node : descendants) { + ASSERT(&node.document() == this); + ASSERT(!node.is_document()); + if (node.parent()) + node.parent()->remove_child(node); + } + } + + m_in_removed_last_ref = false; + decrement_referencing_node_count(); + return; + } + + m_in_removed_last_ref = false; + m_deletion_has_begun = true; + delete this; +} + +Origin Document::origin() const +{ + if (!m_url.is_valid()) + return {}; + 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()) + return; + m_style_update_timer->start(); +} + +void Document::schedule_forced_layout() +{ + if (m_forced_layout_timer->is_active()) + return; + m_forced_layout_timer->start(); +} + +bool Document::is_child_allowed(const Node& node) const +{ + switch (node.type()) { + case NodeType::DOCUMENT_NODE: + case NodeType::TEXT_NODE: + return false; + case NodeType::COMMENT_NODE: + return true; + case NodeType::DOCUMENT_TYPE_NODE: + return !first_child_of_type<DocumentType>(); + case NodeType::ELEMENT_NODE: + return !first_child_of_type<Element>(); + default: + return false; + } +} + +const Element* Document::document_element() const +{ + return first_child_of_type<Element>(); +} + +const HTML::HTMLHtmlElement* Document::html_element() const +{ + auto* html = document_element(); + if (is<HTML::HTMLHtmlElement>(html)) + return downcast<HTML::HTMLHtmlElement>(html); + return nullptr; +} + +const HTML::HTMLHeadElement* Document::head() const +{ + auto* html = html_element(); + if (!html) + return nullptr; + return html->first_child_of_type<HTML::HTMLHeadElement>(); +} + +const HTML::HTMLElement* Document::body() const +{ + auto* html = html_element(); + if (!html) + return nullptr; + auto* first_body = html->first_child_of_type<HTML::HTMLBodyElement>(); + if (first_body) + return first_body; + auto* first_frameset = html->first_child_of_type<HTML::HTMLFrameSetElement>(); + if (first_frameset) + return first_frameset; + return nullptr; +} + +void Document::set_body(HTML::HTMLElement& new_body) +{ + if (!is<HTML::HTMLBodyElement>(new_body) && !is<HTML::HTMLFrameSetElement>(new_body)) { + // FIXME: throw a "HierarchyRequestError" DOMException. + return; + } + + auto* existing_body = body(); + if (existing_body) { + TODO(); + return; + } + + auto* html = document_element(); + if (!html) { + // FIXME: throw a "HierarchyRequestError" DOMException. + return; + } + + // FIXME: Implement this once there's a non-const first_child_of_type: + // "Otherwise, the body element is null, but there's a document element. Append the new value to the document element." + TODO(); +} + +String Document::title() const +{ + auto* head_element = head(); + if (!head_element) + return {}; + + auto* title_element = head_element->first_child_of_type<HTML::HTMLTitleElement>(); + if (!title_element) + return {}; + + auto raw_title = title_element->text_content(); + + StringBuilder builder; + bool last_was_space = false; + for (auto code_point : Utf8View(raw_title)) { + if (isspace(code_point)) { + last_was_space = true; + } else { + if (last_was_space && !builder.is_empty()) + builder.append(' '); + builder.append_code_point(code_point); + last_was_space = false; + } + } + return builder.to_string(); +} + +void Document::set_title(const String& title) +{ + auto* head_element = const_cast<HTML::HTMLHeadElement*>(head()); + if (!head_element) + return; + + 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)); + head_element->append_child(*title_element); + } + + while (RefPtr<Node> child = title_element->first_child()) + title_element->remove_child(child.release_nonnull()); + + title_element->append_child(adopt(*new Text(*this, title))); + + if (auto* page = this->page()) + page->client().page_did_change_title(title); +} + +void Document::attach_to_frame(Badge<Frame>, Frame& frame) +{ + m_frame = frame; + update_layout(); +} + +void Document::detach_from_frame(Badge<Frame>, Frame& frame) +{ + ASSERT(&frame == m_frame); + tear_down_layout_tree(); + m_frame = nullptr; +} + +void Document::tear_down_layout_tree() +{ + if (!m_layout_root) + return; + + // Gather up all the layout nodes in a vector and detach them from parents + // while the vector keeps them alive. + + NonnullRefPtrVector<Layout::Node> layout_nodes; + + m_layout_root->for_each_in_subtree([&](auto& layout_node) { + layout_nodes.append(layout_node); + return IterationDecision::Continue; + }); + + for (auto& layout_node : layout_nodes) { + if (layout_node.parent()) + layout_node.parent()->remove_child(layout_node); + } + + m_layout_root = nullptr; +} + +Color Document::background_color(const Palette& palette) const +{ + auto default_color = palette.base(); + auto* body_element = body(); + if (!body_element) + return default_color; + + auto* body_layout_node = body_element->layout_node(); + if (!body_layout_node) + return default_color; + + auto color = body_layout_node->computed_values().background_color(); + if (!color.alpha()) + return default_color; + return color; +} + +RefPtr<Gfx::Bitmap> Document::background_image() const +{ + auto* body_element = body(); + if (!body_element) + return {}; + + auto* body_layout_node = body_element->layout_node(); + if (!body_layout_node) + return {}; + + auto background_image = body_layout_node->background_image(); + if (!background_image) + return {}; + return background_image->bitmap(); +} + +URL Document::complete_url(const String& string) const +{ + return m_url.complete_url(string); +} + +void Document::invalidate_layout() +{ + tear_down_layout_tree(); +} + +void Document::force_layout() +{ + invalidate_layout(); + update_layout(); +} + +void Document::update_layout() +{ + if (!frame()) + return; + + if (!m_layout_root) { + Layout::TreeBuilder tree_builder; + m_layout_root = static_ptr_cast<Layout::InitialContainingBlockBox>(tree_builder.build(*this)); + } + + Layout::BlockFormattingContext root_formatting_context(*m_layout_root, nullptr); + root_formatting_context.run(*m_layout_root, Layout::LayoutMode::Default); + + m_layout_root->set_needs_display(); + + if (frame()->is_main_frame()) { + if (auto* page = this->page()) + page->client().page_did_layout(); + } +} + +static void update_style_recursively(DOM::Node& node) +{ + node.for_each_child([&](auto& child) { + if (child.needs_style_update()) { + if (is<Element>(child)) + downcast<Element>(child).recompute_style(); + child.set_needs_style_update(false); + } + if (child.child_needs_style_update()) { + update_style_recursively(child); + child.set_child_needs_style_update(false); + } + return IterationDecision::Continue; + }); +} + +void Document::update_style() +{ + update_style_recursively(*this); + update_layout(); +} + +RefPtr<Layout::Node> Document::create_layout_node() +{ + return adopt(*new Layout::InitialContainingBlockBox(*this, CSS::StyleProperties::create())); +} + +void Document::set_link_color(Color color) +{ + m_link_color = color; +} + +void Document::set_active_link_color(Color color) +{ + m_active_link_color = color; +} + +void Document::set_visited_link_color(Color color) +{ + m_visited_link_color = color; +} + +const Layout::InitialContainingBlockBox* Document::layout_node() const +{ + return static_cast<const Layout::InitialContainingBlockBox*>(Node::layout_node()); +} + +Layout::InitialContainingBlockBox* Document::layout_node() +{ + return static_cast<Layout::InitialContainingBlockBox*>(Node::layout_node()); +} + +void Document::set_inspected_node(Node* node) +{ + if (m_inspected_node == node) + return; + + if (m_inspected_node && m_inspected_node->layout_node()) + m_inspected_node->layout_node()->set_needs_display(); + + m_inspected_node = node; + + if (m_inspected_node && m_inspected_node->layout_node()) + m_inspected_node->layout_node()->set_needs_display(); +} + +void Document::set_hovered_node(Node* node) +{ + if (m_hovered_node == node) + return; + + RefPtr<Node> old_hovered_node = move(m_hovered_node); + m_hovered_node = node; + + invalidate_style(); +} + +NonnullRefPtrVector<Element> Document::get_elements_by_name(const String& name) const +{ + NonnullRefPtrVector<Element> elements; + for_each_in_subtree_of_type<Element>([&](auto& element) { + if (element.attribute(HTML::AttributeNames::name) == name) + elements.append(element); + return IterationDecision::Continue; + }); + return elements; +} + +NonnullRefPtrVector<Element> Document::get_elements_by_tag_name(const FlyString& tag_name) const +{ + NonnullRefPtrVector<Element> elements; + for_each_in_subtree_of_type<Element>([&](auto& element) { + if (element.local_name() == tag_name) + elements.append(element); + return IterationDecision::Continue; + }); + return elements; +} + +NonnullRefPtrVector<Element> Document::get_elements_by_class_name(const FlyString& class_name) const +{ + NonnullRefPtrVector<Element> elements; + for_each_in_subtree_of_type<Element>([&](auto& element) { + if (element.has_class(class_name)) + elements.append(element); + return IterationDecision::Continue; + }); + return elements; +} + +Color Document::link_color() const +{ + if (m_link_color.has_value()) + return m_link_color.value(); + if (!page()) + return Color::Blue; + return page()->palette().link(); +} + +Color Document::active_link_color() const +{ + if (m_active_link_color.has_value()) + return m_active_link_color.value(); + if (!page()) + return Color::Red; + return page()->palette().active_link(); +} + +Color Document::visited_link_color() const +{ + if (m_visited_link_color.has_value()) + return m_visited_link_color.value(); + if (!page()) + return Color::Magenta; + return page()->palette().visited_link(); +} + +static JS::VM& main_thread_vm() +{ + static RefPtr<JS::VM> vm; + if (!vm) { + vm = JS::VM::create(); + vm->set_should_log_exceptions(true); + } + return *vm; +} + +JS::Interpreter& Document::interpreter() +{ + if (!m_interpreter) + m_interpreter = JS::Interpreter::create<Bindings::WindowObject>(main_thread_vm(), *m_window); + return *m_interpreter; +} + +JS::Value Document::run_javascript(const StringView& source) +{ + auto parser = JS::Parser(JS::Lexer(source)); + auto program = parser.parse_program(); + if (parser.has_errors()) { + parser.print_errors(); + return JS::js_undefined(); + } + auto& interpreter = document().interpreter(); + auto result = interpreter.run(interpreter.global_object(), *program); + if (interpreter.exception()) + interpreter.vm().clear_exception(); + return result; +} + +NonnullRefPtr<Element> Document::create_element(const String& 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); +} + +NonnullRefPtr<DocumentFragment> Document::create_document_fragment() +{ + return adopt(*new DocumentFragment(*this)); +} + +NonnullRefPtr<Text> Document::create_text_node(const String& data) +{ + return adopt(*new Text(*this, data)); +} + +NonnullRefPtr<Comment> Document::create_comment(const String& data) +{ + return adopt(*new Comment(*this, data)); +} + +void Document::set_pending_parsing_blocking_script(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement* script) +{ + m_pending_parsing_blocking_script = script; +} + +NonnullRefPtr<HTML::HTMLScriptElement> Document::take_pending_parsing_blocking_script(Badge<HTML::HTMLDocumentParser>) +{ + return m_pending_parsing_blocking_script.release_nonnull(); +} + +void Document::add_script_to_execute_when_parsing_has_finished(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement& script) +{ + m_scripts_to_execute_when_parsing_has_finished.append(script); +} + +NonnullRefPtrVector<HTML::HTMLScriptElement> Document::take_scripts_to_execute_when_parsing_has_finished(Badge<HTML::HTMLDocumentParser>) +{ + return move(m_scripts_to_execute_when_parsing_has_finished); +} + +void Document::add_script_to_execute_as_soon_as_possible(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement& script) +{ + m_scripts_to_execute_as_soon_as_possible.append(script); +} + +NonnullRefPtrVector<HTML::HTMLScriptElement> Document::take_scripts_to_execute_as_soon_as_possible(Badge<HTML::HTMLDocumentParser>) +{ + return move(m_scripts_to_execute_as_soon_as_possible); +} + +void Document::adopt_node(Node& subtree_root) +{ + subtree_root.for_each_in_subtree([&](auto& node) { + node.set_document({}, *this); + return IterationDecision::Continue; + }); +} + +const DocumentType* Document::doctype() const +{ + return first_child_of_type<DocumentType>(); +} + +const String& Document::compat_mode() const +{ + static String back_compat = "BackCompat"; + static String css1_compat = "CSS1Compat"; + + if (m_quirks_mode == QuirksMode::Yes) + return back_compat; + + return css1_compat; +} + +bool Document::is_editable() const +{ + return m_editable; +} + +void Document::set_focused_element(Element* element) +{ + if (m_focused_element == element) + return; + + m_focused_element = element; + + if (m_layout_root) + m_layout_root->set_needs_display(); +} + +void Document::set_ready_state(const String& ready_state) +{ + m_ready_state = ready_state; + dispatch_event(Event::create(HTML::EventNames::readystatechange)); +} + +Page* Document::page() +{ + return m_frame ? m_frame->page() : nullptr; +} + +const Page* Document::page() const +{ + return m_frame ? m_frame->page() : nullptr; +} + +EventTarget* Document::get_parent(const Event& event) +{ + if (event.type() == HTML::EventNames::load) + return nullptr; + + return &window(); +} + +void Document::completely_finish_loading() +{ + // FIXME: This needs to handle iframes. + dispatch_event(DOM::Event::create(HTML::EventNames::load)); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h new file mode 100644 index 0000000000..a46413758d --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Document.h @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2018-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 <AK/FlyString.h> +#include <AK/Function.h> +#include <AK/NonnullRefPtrVector.h> +#include <AK/OwnPtr.h> +#include <AK/String.h> +#include <AK/URL.h> +#include <AK/WeakPtr.h> +#include <LibCore/Forward.h> +#include <LibJS/Forward.h> +#include <LibWeb/Bindings/ScriptExecutionContext.h> +#include <LibWeb/CSS/StyleResolver.h> +#include <LibWeb/CSS/StyleSheet.h> +#include <LibWeb/CSS/StyleSheetList.h> +#include <LibWeb/DOM/DOMImplementation.h> +#include <LibWeb/DOM/NonElementParentNode.h> +#include <LibWeb/DOM/ParentNode.h> + +namespace Web::DOM { + +enum class QuirksMode { + No, + Limited, + Yes +}; + +class Document + : public ParentNode + , public NonElementParentNode<Document> + , public Bindings::ScriptExecutionContext { +public: + using WrapperType = Bindings::DocumentWrapper; + + static NonnullRefPtr<Document> create(const URL& url = "about:blank") { return adopt(*new Document(url)); } + virtual ~Document() override; + + bool should_invalidate_styles_on_attribute_changes() const { return m_should_invalidate_styles_on_attribute_changes; } + void set_should_invalidate_styles_on_attribute_changes(bool b) { m_should_invalidate_styles_on_attribute_changes = b; } + + void set_url(const URL& url) { m_url = url; } + URL url() const { return m_url; } + + Origin origin() const; + void set_origin(const Origin& origin); + + bool is_scripting_enabled() const { return true; } + + URL complete_url(const String&) const; + + CSS::StyleResolver& style_resolver() { return *m_style_resolver; } + const CSS::StyleResolver& style_resolver() const { return *m_style_resolver; } + + CSS::StyleSheetList& style_sheets() { return *m_style_sheets; } + const CSS::StyleSheetList& style_sheets() const { return *m_style_sheets; } + + virtual FlyString node_name() const override { return "#document"; } + + void set_hovered_node(Node*); + Node* hovered_node() { return m_hovered_node; } + const Node* hovered_node() const { return m_hovered_node; } + + void set_inspected_node(Node*); + Node* inspected_node() { return m_inspected_node; } + const Node* inspected_node() const { return m_inspected_node; } + + const Element* document_element() const; + const HTML::HTMLHtmlElement* html_element() const; + const HTML::HTMLHeadElement* head() const; + const HTML::HTMLElement* body() const; + void set_body(HTML::HTMLElement& new_body); + + String title() const; + void set_title(const String&); + + void attach_to_frame(Badge<Frame>, Frame&); + void detach_from_frame(Badge<Frame>, Frame&); + + Frame* frame() { return m_frame.ptr(); } + const Frame* frame() const { return m_frame.ptr(); } + + Page* page(); + const Page* page() const; + + Color background_color(const Gfx::Palette&) const; + RefPtr<Gfx::Bitmap> background_image() const; + + Color link_color() const; + void set_link_color(Color); + + Color active_link_color() const; + void set_active_link_color(Color); + + Color visited_link_color() const; + void set_visited_link_color(Color); + + void force_layout(); + void invalidate_layout(); + + void update_style(); + void update_layout(); + + virtual bool is_child_allowed(const Node&) const override; + + const Layout::InitialContainingBlockBox* layout_node() const; + Layout::InitialContainingBlockBox* layout_node(); + + void schedule_style_update(); + void schedule_forced_layout(); + + NonnullRefPtrVector<Element> get_elements_by_name(const String&) const; + NonnullRefPtrVector<Element> get_elements_by_tag_name(const FlyString&) const; + NonnullRefPtrVector<Element> get_elements_by_class_name(const FlyString&) const; + + const String& source() const { return m_source; } + void set_source(const String& source) { m_source = source; } + + virtual JS::Interpreter& interpreter() override; + + JS::Value run_javascript(const StringView&); + + NonnullRefPtr<Element> create_element(const String& tag_name); + NonnullRefPtr<DocumentFragment> create_document_fragment(); + NonnullRefPtr<Text> create_text_node(const String& data); + NonnullRefPtr<Comment> create_comment(const String& data); + + void set_pending_parsing_blocking_script(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement*); + HTML::HTMLScriptElement* pending_parsing_blocking_script() { return m_pending_parsing_blocking_script; } + NonnullRefPtr<HTML::HTMLScriptElement> take_pending_parsing_blocking_script(Badge<HTML::HTMLDocumentParser>); + + void add_script_to_execute_when_parsing_has_finished(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement&); + NonnullRefPtrVector<HTML::HTMLScriptElement> take_scripts_to_execute_when_parsing_has_finished(Badge<HTML::HTMLDocumentParser>); + + void add_script_to_execute_as_soon_as_possible(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement&); + NonnullRefPtrVector<HTML::HTMLScriptElement> take_scripts_to_execute_as_soon_as_possible(Badge<HTML::HTMLDocumentParser>); + + QuirksMode mode() const { return m_quirks_mode; } + bool in_quirks_mode() const { return m_quirks_mode == QuirksMode::Yes; } + void set_quirks_mode(QuirksMode mode) { m_quirks_mode = mode; } + + void adopt_node(Node&); + + const DocumentType* doctype() const; + const String& compat_mode() const; + + void set_editable(bool editable) { m_editable = editable; } + virtual bool is_editable() const final; + + Element* focused_element() { return m_focused_element; } + const Element* focused_element() const { return m_focused_element; } + + void set_focused_element(Element*); + + bool created_for_appropriate_template_contents() const { return m_created_for_appropriate_template_contents; } + void set_created_for_appropriate_template_contents(bool value) { m_created_for_appropriate_template_contents = value; } + + Document* associated_inert_template_document() { return m_associated_inert_template_document; } + const Document* associated_inert_template_document() const { return m_associated_inert_template_document; } + void set_associated_inert_template_document(Document& document) { m_associated_inert_template_document = document; } + + const String& ready_state() const { return m_ready_state; } + void set_ready_state(const String&); + + void ref_from_node(Badge<Node>) + { + increment_referencing_node_count(); + } + + void unref_from_node(Badge<Node>) + { + decrement_referencing_node_count(); + } + + void removed_last_ref(); + + Window& window() { return *m_window; } + + const String& content_type() const { return m_content_type; } + void set_content_type(const String& content_type) { m_content_type = content_type; } + + const String& encoding() const { return m_encoding; } + void set_encoding(const String& encoding) { m_encoding = encoding; } + + // NOTE: These are intended for the JS bindings + const String& character_set() const { return encoding(); } + const String& charset() const { return encoding(); } + const String& input_encoding() const { return encoding(); } + + bool ready_for_post_load_tasks() const { return m_ready_for_post_load_tasks; } + void set_ready_for_post_load_tasks(bool ready) { m_ready_for_post_load_tasks = ready; } + + void completely_finish_loading(); + + const NonnullRefPtr<DOMImplementation> implementation() const { return m_implementation; } + + virtual EventTarget* get_parent(const Event&) override; + +private: + explicit Document(const URL&); + + virtual RefPtr<Layout::Node> create_layout_node() override; + + void tear_down_layout_tree(); + + void increment_referencing_node_count() + { + ASSERT(!m_deletion_has_begun); + ++m_referencing_node_count; + } + + void decrement_referencing_node_count() + { + ASSERT(!m_deletion_has_begun); + ASSERT(m_referencing_node_count); + --m_referencing_node_count; + if (!m_referencing_node_count && !ref_count()) { + m_deletion_has_begun = true; + delete this; + } + } + + unsigned m_referencing_node_count { 0 }; + + OwnPtr<CSS::StyleResolver> m_style_resolver; + RefPtr<CSS::StyleSheetList> m_style_sheets; + RefPtr<Node> m_hovered_node; + RefPtr<Node> m_inspected_node; + WeakPtr<Frame> m_frame; + URL m_url; + + RefPtr<Window> m_window; + + RefPtr<Layout::InitialContainingBlockBox> m_layout_root; + + Optional<Color> m_link_color; + Optional<Color> m_active_link_color; + Optional<Color> m_visited_link_color; + + RefPtr<Core::Timer> m_style_update_timer; + RefPtr<Core::Timer> m_forced_layout_timer; + + String m_source; + + OwnPtr<JS::Interpreter> m_interpreter; + + RefPtr<HTML::HTMLScriptElement> m_pending_parsing_blocking_script; + NonnullRefPtrVector<HTML::HTMLScriptElement> m_scripts_to_execute_when_parsing_has_finished; + NonnullRefPtrVector<HTML::HTMLScriptElement> m_scripts_to_execute_as_soon_as_possible; + + QuirksMode m_quirks_mode { QuirksMode::No }; + bool m_editable { false }; + + WeakPtr<Element> m_focused_element; + + bool m_created_for_appropriate_template_contents { false }; + RefPtr<Document> m_associated_inert_template_document; + + String m_ready_state { "loading" }; + String m_content_type { "application/xml" }; + String m_encoding { "UTF-8" }; + + bool m_ready_for_post_load_tasks { false }; + + NonnullRefPtr<DOMImplementation> m_implementation; + + bool m_should_invalidate_styles_on_attribute_changes { true }; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/Document.idl b/Userland/Libraries/LibWeb/DOM/Document.idl new file mode 100644 index 0000000000..3b34314e8d --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Document.idl @@ -0,0 +1,37 @@ +interface Document : Node { + + readonly attribute DOMImplementation implementation; + + readonly attribute DOMString characterSet; + readonly attribute DOMString charset; + readonly attribute DOMString inputEncoding; + readonly attribute DOMString contentType; + + Element? getElementById(DOMString id); + ArrayFromVector getElementsByName(DOMString name); + ArrayFromVector getElementsByTagName(DOMString tagName); + ArrayFromVector getElementsByClassName(DOMString className); + + readonly attribute Element? firstElementChild; + readonly attribute Element? lastElementChild; + + Element? querySelector(DOMString selectors); + ArrayFromVector querySelectorAll(DOMString selectors); + + Element createElement(DOMString tagName); + DocumentFragment createDocumentFragment(); + Text createTextNode(DOMString data); + Comment createComment(DOMString data); + + readonly attribute DOMString compatMode; + readonly attribute DocumentType? doctype; + + readonly attribute Element? documentElement; + attribute HTMLElement? body; + readonly attribute HTMLHeadElement? head; + + readonly attribute DOMString readyState; + + attribute DOMString title; + +}; diff --git a/Userland/Libraries/LibWeb/DOM/DocumentFragment.cpp b/Userland/Libraries/LibWeb/DOM/DocumentFragment.cpp new file mode 100644 index 0000000000..92400feb70 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/DocumentFragment.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020, Luke Wilde <luke.wilde@live.co.uk> + * 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 <LibWeb/DOM/DocumentFragment.h> + +namespace Web::DOM { + +DocumentFragment::DocumentFragment(Document& document) + : ParentNode(document, NodeType::DOCUMENT_FRAGMENT_NODE) +{ +} + +DocumentFragment::~DocumentFragment() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/DocumentFragment.h b/Userland/Libraries/LibWeb/DOM/DocumentFragment.h new file mode 100644 index 0000000000..b5dbc81699 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/DocumentFragment.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018-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 <AK/FlyString.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/DOM/NonElementParentNode.h> +#include <LibWeb/DOM/ParentNode.h> + +namespace Web::DOM { + +class DocumentFragment + : public ParentNode + , public NonElementParentNode<DocumentFragment> { +public: + using WrapperType = Bindings::DocumentFragmentWrapper; + + explicit DocumentFragment(Document& document); + virtual ~DocumentFragment() override; + + virtual FlyString node_name() const override { return "#document-fragment"; } + + RefPtr<Element> host() { return m_host; } + const RefPtr<Element> host() const { return m_host; } + + void set_host(Element& host) { m_host = host; } + +private: + RefPtr<Element> m_host; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/DocumentFragment.idl b/Userland/Libraries/LibWeb/DOM/DocumentFragment.idl new file mode 100644 index 0000000000..fc18e968be --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/DocumentFragment.idl @@ -0,0 +1,11 @@ +interface DocumentFragment : Node { + + Element? getElementById(DOMString id); + + readonly attribute Element? firstElementChild; + readonly attribute Element? lastElementChild; + + Element? querySelector(DOMString selectors); + ArrayFromVector querySelectorAll(DOMString selectors); + +}; diff --git a/Userland/Libraries/LibWeb/DOM/DocumentType.cpp b/Userland/Libraries/LibWeb/DOM/DocumentType.cpp new file mode 100644 index 0000000000..da0f176efc --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/DocumentType.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-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 <LibWeb/DOM/DocumentType.h> + +namespace Web::DOM { + +DocumentType::DocumentType(Document& document) + : Node(document, NodeType::DOCUMENT_TYPE_NODE) +{ +} + +DocumentType::~DocumentType() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/DocumentType.h b/Userland/Libraries/LibWeb/DOM/DocumentType.h new file mode 100644 index 0000000000..ffc4d82150 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/DocumentType.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-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 <AK/FlyString.h> +#include <LibWeb/DOM/Node.h> + +namespace Web::DOM { + +class DocumentType final : public Node { +public: + using WrapperType = Bindings::DocumentTypeWrapper; + + explicit DocumentType(Document&); + virtual ~DocumentType() override; + + virtual FlyString node_name() const override { return "#doctype"; } + + const String& name() const { return m_name; } + void set_name(const String& name) { m_name = name; } + + const String& public_id() const { return m_public_id; } + void set_public_id(const String& public_id) { m_public_id = public_id; } + + const String& system_id() const { return m_system_id; } + void set_system_id(const String& system_id) { m_system_id = system_id; } + +private: + String m_name; + String m_public_id; + String m_system_id; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/DocumentType.idl b/Userland/Libraries/LibWeb/DOM/DocumentType.idl new file mode 100644 index 0000000000..813b0e5b47 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/DocumentType.idl @@ -0,0 +1,7 @@ +interface DocumentType : Node { + + readonly attribute DOMString name; + readonly attribute DOMString publicId; + readonly attribute DOMString systemId; + +}; diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp new file mode 100644 index 0000000000..094d877475 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Element.cpp @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2018-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/StringBuilder.h> +#include <LibWeb/CSS/Length.h> +#include <LibWeb/CSS/Parser/CSSParser.h> +#include <LibWeb/CSS/PropertyID.h> +#include <LibWeb/CSS/StyleInvalidator.h> +#include <LibWeb/CSS/StyleResolver.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/DocumentFragment.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/DOM/Text.h> +#include <LibWeb/Dump.h> +#include <LibWeb/HTML/Parser/HTMLDocumentParser.h> +#include <LibWeb/Layout/BlockBox.h> +#include <LibWeb/Layout/InlineNode.h> +#include <LibWeb/Layout/ListItemBox.h> +#include <LibWeb/Layout/TableBox.h> +#include <LibWeb/Layout/TableCellBox.h> +#include <LibWeb/Layout/TableRowBox.h> +#include <LibWeb/Layout/TableRowGroupBox.h> +#include <LibWeb/Layout/TreeBuilder.h> +#include <LibWeb/Layout/WidgetBox.h> + +namespace Web::DOM { + +Element::Element(Document& document, const QualifiedName& qualified_name) + : ParentNode(document, NodeType::ELEMENT_NODE) + , m_qualified_name(qualified_name) +{ +} + +Element::~Element() +{ +} + +Attribute* Element::find_attribute(const FlyString& name) +{ + for (auto& attribute : m_attributes) { + if (attribute.name() == name) + return &attribute; + } + return nullptr; +} + +const Attribute* Element::find_attribute(const FlyString& name) const +{ + for (auto& attribute : m_attributes) { + if (attribute.name() == name) + return &attribute; + } + return nullptr; +} + +String Element::attribute(const FlyString& name) const +{ + if (auto* attribute = find_attribute(name)) + return attribute->value(); + return {}; +} + +void Element::set_attribute(const FlyString& name, const String& value) +{ + CSS::StyleInvalidator style_invalidator(document()); + + if (auto* attribute = find_attribute(name)) + attribute->set_value(value); + else + m_attributes.empend(name, value); + + parse_attribute(name, value); +} + +void Element::remove_attribute(const FlyString& name) +{ + CSS::StyleInvalidator style_invalidator(document()); + + m_attributes.remove_first_matching([&](auto& attribute) { return attribute.name() == name; }); +} + +bool Element::has_class(const FlyString& class_name) const +{ + for (auto& class_ : m_classes) { + if (class_ == class_name) + return true; + } + return false; +} + +RefPtr<Layout::Node> Element::create_layout_node() +{ + auto style = document().style_resolver().resolve_style(*this); + const_cast<Element&>(*this).m_specified_css_values = style; + auto display = style->display(); + + if (display == CSS::Display::None) + return nullptr; + + if (local_name() == "noscript" && document().is_scripting_enabled()) + return nullptr; + + if (display == CSS::Display::Block) + return adopt(*new Layout::BlockBox(document(), this, move(style))); + + if (display == CSS::Display::Inline) { + if (style->float_().value_or(CSS::Float::None) != CSS::Float::None) + return adopt(*new Layout::BlockBox(document(), this, move(style))); + return adopt(*new Layout::InlineNode(document(), *this, move(style))); + } + + if (display == CSS::Display::ListItem) + return adopt(*new Layout::ListItemBox(document(), *this, move(style))); + if (display == CSS::Display::Table) + return adopt(*new Layout::TableBox(document(), this, move(style))); + if (display == CSS::Display::TableRow) + return adopt(*new Layout::TableRowBox(document(), this, move(style))); + if (display == CSS::Display::TableCell) + return adopt(*new Layout::TableCellBox(document(), this, move(style))); + if (display == CSS::Display::TableRowGroup || display == CSS::Display::TableHeaderGroup || display == CSS::Display::TableFooterGroup) + return adopt(*new Layout::TableRowGroupBox(document(), *this, move(style))); + if (display == CSS::Display::InlineBlock) { + auto inline_block = adopt(*new Layout::BlockBox(document(), this, move(style))); + inline_block->set_inline(true); + return inline_block; + } + ASSERT_NOT_REACHED(); +} + +void Element::parse_attribute(const FlyString& name, const String& value) +{ + if (name == HTML::AttributeNames::class_) { + auto new_classes = value.split_view(' '); + m_classes.clear(); + m_classes.ensure_capacity(new_classes.size()); + for (auto& new_class : new_classes) { + m_classes.unchecked_append(new_class); + } + } else if (name == HTML::AttributeNames::style) { + m_inline_style = parse_css_declaration(CSS::ParsingContext(document()), value); + set_needs_style_update(true); + } +} + +enum class StyleDifference { + None, + NeedsRepaint, + NeedsRelayout, +}; + +static StyleDifference compute_style_difference(const CSS::StyleProperties& old_style, const CSS::StyleProperties& new_style, const Document& document) +{ + if (old_style == new_style) + return StyleDifference::None; + + bool needs_repaint = false; + bool needs_relayout = false; + + if (new_style.display() != old_style.display()) + needs_relayout = true; + + if (new_style.color_or_fallback(CSS::PropertyID::Color, document, Color::Black) != old_style.color_or_fallback(CSS::PropertyID::Color, document, Color::Black)) + needs_repaint = true; + else if (new_style.color_or_fallback(CSS::PropertyID::BackgroundColor, document, Color::Black) != old_style.color_or_fallback(CSS::PropertyID::BackgroundColor, document, Color::Black)) + needs_repaint = true; + + if (needs_relayout) + return StyleDifference::NeedsRelayout; + if (needs_repaint) + return StyleDifference::NeedsRepaint; + return StyleDifference::None; +} + +void Element::recompute_style() +{ + set_needs_style_update(false); + ASSERT(parent()); + auto old_specified_css_values = m_specified_css_values; + auto new_specified_css_values = document().style_resolver().resolve_style(*this); + m_specified_css_values = new_specified_css_values; + if (!layout_node()) { + if (new_specified_css_values->display() == CSS::Display::None) + return; + // We need a new layout tree here! + Layout::TreeBuilder tree_builder; + tree_builder.build(*this); + return; + } + + // Don't bother with style on widgets. NATIVE LOOK & FEEL BABY! + if (is<Layout::WidgetBox>(layout_node())) + return; + + auto diff = StyleDifference::NeedsRelayout; + if (old_specified_css_values) + diff = compute_style_difference(*old_specified_css_values, *new_specified_css_values, document()); + if (diff == StyleDifference::None) + return; + layout_node()->apply_style(*new_specified_css_values); + if (diff == StyleDifference::NeedsRelayout) { + document().schedule_forced_layout(); + return; + } + if (diff == StyleDifference::NeedsRepaint) { + layout_node()->set_needs_display(); + } +} + +NonnullRefPtr<CSS::StyleProperties> Element::computed_style() +{ + // FIXME: This implementation is not doing anything it's supposed to. + auto properties = m_specified_css_values->clone(); + if (layout_node() && layout_node()->has_style()) { + CSS::PropertyID box_model_metrics[] = { + CSS::PropertyID::MarginTop, + CSS::PropertyID::MarginBottom, + CSS::PropertyID::MarginLeft, + CSS::PropertyID::MarginRight, + CSS::PropertyID::PaddingTop, + CSS::PropertyID::PaddingBottom, + CSS::PropertyID::PaddingLeft, + CSS::PropertyID::PaddingRight, + CSS::PropertyID::BorderTopWidth, + CSS::PropertyID::BorderBottomWidth, + CSS::PropertyID::BorderLeftWidth, + CSS::PropertyID::BorderRightWidth, + }; + for (CSS::PropertyID id : box_model_metrics) { + auto prop = m_specified_css_values->property(id); + if (prop.has_value()) + properties->set_property(id, prop.value()); + } + } + return properties; +} + +void Element::set_inner_html(StringView markup) +{ + auto new_children = HTML::HTMLDocumentParser::parse_html_fragment(*this, markup); + remove_all_children(); + while (!new_children.is_empty()) { + append_child(new_children.take_first()); + } + + set_needs_style_update(true); + document().invalidate_layout(); +} + +String Element::inner_html() const +{ + auto escape_string = [](const StringView& string, bool attribute_mode) -> String { + // https://html.spec.whatwg.org/multipage/parsing.html#escapingString + StringBuilder builder; + for (auto& ch : string) { + if (ch == '&') + builder.append("&"); + // FIXME: also replace U+00A0 NO-BREAK SPACE with + else if (ch == '"' && attribute_mode) + builder.append("""); + else if (ch == '<' && !attribute_mode) + builder.append("<"); + else if (ch == '>' && !attribute_mode) + builder.append(">"); + else + builder.append(ch); + } + return builder.to_string(); + }; + + StringBuilder builder; + + Function<void(const Node&)> recurse = [&](auto& node) { + for (auto* child = node.first_child(); child; child = child->next_sibling()) { + if (child->is_element()) { + auto& element = downcast<Element>(*child); + builder.append('<'); + builder.append(element.local_name()); + element.for_each_attribute([&](auto& name, auto& value) { + builder.append(' '); + builder.append(name); + builder.append('='); + builder.append('"'); + builder.append(escape_string(value, true)); + builder.append('"'); + }); + builder.append('>'); + + recurse(*child); + + // FIXME: This should be skipped for void elements + builder.append("</"); + builder.append(element.local_name()); + builder.append('>'); + } + if (child->is_text()) { + auto& text = downcast<Text>(*child); + builder.append(escape_string(text.data(), false)); + } + // FIXME: Also handle Comment, ProcessingInstruction, DocumentType + } + }; + recurse(*this); + + return builder.to_string(); +} + +bool Element::is_focused() const +{ + return document().focused_element() == this; +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h new file mode 100644 index 0000000000..ddefb2ab93 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Element.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018-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 <AK/FlyString.h> +#include <AK/String.h> +#include <LibWeb/DOM/Attribute.h> +#include <LibWeb/DOM/NonDocumentTypeChildNode.h> +#include <LibWeb/DOM/ParentNode.h> +#include <LibWeb/HTML/AttributeNames.h> +#include <LibWeb/HTML/TagNames.h> +#include <LibWeb/Layout/Node.h> +#include <LibWeb/QualifiedName.h> + +namespace Web::DOM { + +class Element + : public ParentNode + , public NonDocumentTypeChildNode<Element> { + +public: + using WrapperType = Bindings::ElementWrapper; + + Element(Document&, const QualifiedName& qualified_name); + virtual ~Element() override; + + virtual FlyString node_name() const final { return m_qualified_name.local_name(); } + const FlyString& local_name() const { return m_qualified_name.local_name(); } + + // NOTE: This is for the JS bindings + const FlyString& tag_name() const { return local_name(); } + + const FlyString& namespace_() const { return m_qualified_name.namespace_(); } + + // NOTE: This is for the JS bindings + const FlyString& namespace_uri() const { return namespace_(); } + + bool has_attribute(const FlyString& name) const { return !attribute(name).is_null(); } + bool has_attributes() const { return !m_attributes.is_empty(); } + 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 remove_attribute(const FlyString& name); + + template<typename Callback> + void for_each_attribute(Callback callback) const + { + for (auto& attribute : m_attributes) + callback(attribute.name(), attribute.value()); + } + + bool has_class(const FlyString&) const; + const Vector<FlyString>& class_names() const { return m_classes; } + + virtual void apply_presentational_hints(CSS::StyleProperties&) const { } + virtual void parse_attribute(const FlyString& name, const String& value); + + void recompute_style(); + + Layout::NodeWithStyle* layout_node() { return static_cast<Layout::NodeWithStyle*>(Node::layout_node()); } + const Layout::NodeWithStyle* layout_node() const { return static_cast<const Layout::NodeWithStyle*>(Node::layout_node()); } + + String name() const { return attribute(HTML::AttributeNames::name); } + + const CSS::StyleProperties* specified_css_values() const { return m_specified_css_values.ptr(); } + NonnullRefPtr<CSS::StyleProperties> computed_style(); + + const CSS::StyleDeclaration* inline_style() const { return m_inline_style; } + + // FIXME: innerHTML also appears on shadow roots. https://w3c.github.io/DOM-Parsing/#dom-innerhtml + String inner_html() const; + void set_inner_html(StringView); + + bool is_focused() const; + virtual bool is_focusable() const { return false; } + +protected: + RefPtr<Layout::Node> create_layout_node() override; + +private: + Attribute* find_attribute(const FlyString& name); + const Attribute* find_attribute(const FlyString& name) const; + + QualifiedName m_qualified_name; + Vector<Attribute> m_attributes; + + RefPtr<CSS::StyleDeclaration> m_inline_style; + + RefPtr<CSS::StyleProperties> m_specified_css_values; + + Vector<FlyString> m_classes; +}; + +} + +namespace AK { +template<> +inline bool is<Web::DOM::Element>(const Web::DOM::Node& input) +{ + return input.is_element(); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/Element.idl b/Userland/Libraries/LibWeb/DOM/Element.idl new file mode 100644 index 0000000000..e5de36e75b --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Element.idl @@ -0,0 +1,23 @@ +interface Element : Node { + readonly attribute DOMString? namespaceURI; + readonly attribute DOMString tagName; + + DOMString? getAttribute(DOMString qualifiedName); + undefined setAttribute(DOMString qualifiedName, DOMString value); + undefined removeAttribute(DOMString qualifiedName); + boolean hasAttribute(DOMString qualifiedName); + boolean hasAttributes(); + + readonly attribute Element? firstElementChild; + readonly attribute Element? lastElementChild; + + Element? querySelector(DOMString selectors); + ArrayFromVector querySelectorAll(DOMString selectors); + + [LegacyNullToEmptyString] attribute DOMString innerHTML; + [Reflect] attribute DOMString id; + [Reflect=class] attribute DOMString className; + + readonly attribute Element? nextElementSibling; + readonly attribute Element? previousElementSibling; +}; diff --git a/Userland/Libraries/LibWeb/DOM/ElementFactory.cpp b/Userland/Libraries/LibWeb/DOM/ElementFactory.cpp new file mode 100644 index 0000000000..810d82efdc --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/ElementFactory.cpp @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2020, Luke Wilde <luke.wilde@live.co.uk> + * 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 <LibWeb/DOM/ElementFactory.h> +#include <LibWeb/HTML/HTMLAnchorElement.h> +#include <LibWeb/HTML/HTMLAreaElement.h> +#include <LibWeb/HTML/HTMLAudioElement.h> +#include <LibWeb/HTML/HTMLBRElement.h> +#include <LibWeb/HTML/HTMLBaseElement.h> +#include <LibWeb/HTML/HTMLBlinkElement.h> +#include <LibWeb/HTML/HTMLBodyElement.h> +#include <LibWeb/HTML/HTMLButtonElement.h> +#include <LibWeb/HTML/HTMLCanvasElement.h> +#include <LibWeb/HTML/HTMLDListElement.h> +#include <LibWeb/HTML/HTMLDataElement.h> +#include <LibWeb/HTML/HTMLDataListElement.h> +#include <LibWeb/HTML/HTMLDetailsElement.h> +#include <LibWeb/HTML/HTMLDialogElement.h> +#include <LibWeb/HTML/HTMLDirectoryElement.h> +#include <LibWeb/HTML/HTMLDivElement.h> +#include <LibWeb/HTML/HTMLEmbedElement.h> +#include <LibWeb/HTML/HTMLFieldSetElement.h> +#include <LibWeb/HTML/HTMLFontElement.h> +#include <LibWeb/HTML/HTMLFormElement.h> +#include <LibWeb/HTML/HTMLFrameElement.h> +#include <LibWeb/HTML/HTMLFrameSetElement.h> +#include <LibWeb/HTML/HTMLHRElement.h> +#include <LibWeb/HTML/HTMLHeadElement.h> +#include <LibWeb/HTML/HTMLHeadingElement.h> +#include <LibWeb/HTML/HTMLHtmlElement.h> +#include <LibWeb/HTML/HTMLIFrameElement.h> +#include <LibWeb/HTML/HTMLImageElement.h> +#include <LibWeb/HTML/HTMLInputElement.h> +#include <LibWeb/HTML/HTMLLIElement.h> +#include <LibWeb/HTML/HTMLLabelElement.h> +#include <LibWeb/HTML/HTMLLegendElement.h> +#include <LibWeb/HTML/HTMLLinkElement.h> +#include <LibWeb/HTML/HTMLMapElement.h> +#include <LibWeb/HTML/HTMLMarqueeElement.h> +#include <LibWeb/HTML/HTMLMenuElement.h> +#include <LibWeb/HTML/HTMLMetaElement.h> +#include <LibWeb/HTML/HTMLMeterElement.h> +#include <LibWeb/HTML/HTMLModElement.h> +#include <LibWeb/HTML/HTMLOListElement.h> +#include <LibWeb/HTML/HTMLObjectElement.h> +#include <LibWeb/HTML/HTMLOptGroupElement.h> +#include <LibWeb/HTML/HTMLOptionElement.h> +#include <LibWeb/HTML/HTMLOutputElement.h> +#include <LibWeb/HTML/HTMLParagraphElement.h> +#include <LibWeb/HTML/HTMLParamElement.h> +#include <LibWeb/HTML/HTMLPictureElement.h> +#include <LibWeb/HTML/HTMLPreElement.h> +#include <LibWeb/HTML/HTMLProgressElement.h> +#include <LibWeb/HTML/HTMLQuoteElement.h> +#include <LibWeb/HTML/HTMLScriptElement.h> +#include <LibWeb/HTML/HTMLSelectElement.h> +#include <LibWeb/HTML/HTMLSlotElement.h> +#include <LibWeb/HTML/HTMLSourceElement.h> +#include <LibWeb/HTML/HTMLSpanElement.h> +#include <LibWeb/HTML/HTMLStyleElement.h> +#include <LibWeb/HTML/HTMLTableCaptionElement.h> +#include <LibWeb/HTML/HTMLTableCellElement.h> +#include <LibWeb/HTML/HTMLTableColElement.h> +#include <LibWeb/HTML/HTMLTableElement.h> +#include <LibWeb/HTML/HTMLTableRowElement.h> +#include <LibWeb/HTML/HTMLTableSectionElement.h> +#include <LibWeb/HTML/HTMLTemplateElement.h> +#include <LibWeb/HTML/HTMLTextAreaElement.h> +#include <LibWeb/HTML/HTMLTimeElement.h> +#include <LibWeb/HTML/HTMLTitleElement.h> +#include <LibWeb/HTML/HTMLTrackElement.h> +#include <LibWeb/HTML/HTMLUListElement.h> +#include <LibWeb/HTML/HTMLUnknownElement.h> +#include <LibWeb/HTML/HTMLVideoElement.h> +#include <LibWeb/SVG/SVGPathElement.h> +#include <LibWeb/SVG/SVGSVGElement.h> +#include <LibWeb/SVG/TagNames.h> + +namespace Web::DOM { + +NonnullRefPtr<Element> create_element(Document& document, const FlyString& tag_name, const FlyString& namespace_) +{ + auto lowercase_tag_name = tag_name.to_lowercase(); + // FIXME: Add prefix when we support it. + auto qualified_name = QualifiedName(tag_name, {}, namespace_); + if (lowercase_tag_name == HTML::TagNames::a) + return adopt(*new HTML::HTMLAnchorElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::area) + return adopt(*new HTML::HTMLAreaElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::audio) + return adopt(*new HTML::HTMLAudioElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::base) + return adopt(*new HTML::HTMLBaseElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::blink) + return adopt(*new HTML::HTMLBlinkElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::body) + return adopt(*new HTML::HTMLBodyElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::br) + return adopt(*new HTML::HTMLBRElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::button) + return adopt(*new HTML::HTMLButtonElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::canvas) + return adopt(*new HTML::HTMLCanvasElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::data) + return adopt(*new HTML::HTMLDataElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::datalist) + return adopt(*new HTML::HTMLDataListElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::details) + return adopt(*new HTML::HTMLDetailsElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::dialog) + return adopt(*new HTML::HTMLDialogElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::dir) + return adopt(*new HTML::HTMLDirectoryElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::div) + return adopt(*new HTML::HTMLDivElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::dl) + return adopt(*new HTML::HTMLDListElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::embed) + return adopt(*new HTML::HTMLEmbedElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::fieldset) + return adopt(*new HTML::HTMLFieldSetElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::font) + return adopt(*new HTML::HTMLFontElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::form) + return adopt(*new HTML::HTMLFormElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::frame) + return adopt(*new HTML::HTMLFrameElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::frameset) + return adopt(*new HTML::HTMLFrameSetElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::head) + return adopt(*new HTML::HTMLHeadElement(document, qualified_name)); + if (lowercase_tag_name.is_one_of(HTML::TagNames::h1, HTML::TagNames::h2, HTML::TagNames::h3, HTML::TagNames::h4, HTML::TagNames::h5, HTML::TagNames::h6)) + return adopt(*new HTML::HTMLHeadingElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::hr) + return adopt(*new HTML::HTMLHRElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::html) + return adopt(*new HTML::HTMLHtmlElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::iframe) + return adopt(*new HTML::HTMLIFrameElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::img) + return adopt(*new HTML::HTMLImageElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::input) + return adopt(*new HTML::HTMLInputElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::label) + return adopt(*new HTML::HTMLLabelElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::legend) + return adopt(*new HTML::HTMLLegendElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::li) + return adopt(*new HTML::HTMLLIElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::link) + return adopt(*new HTML::HTMLLinkElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::map) + return adopt(*new HTML::HTMLMapElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::marquee) + return adopt(*new HTML::HTMLMarqueeElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::menu) + return adopt(*new HTML::HTMLMenuElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::meta) + return adopt(*new HTML::HTMLMetaElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::meter) + return adopt(*new HTML::HTMLMeterElement(document, qualified_name)); + if (lowercase_tag_name.is_one_of(HTML::TagNames::ins, HTML::TagNames::del)) + return adopt(*new HTML::HTMLModElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::object) + return adopt(*new HTML::HTMLObjectElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::ol) + return adopt(*new HTML::HTMLOListElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::optgroup) + return adopt(*new HTML::HTMLOptGroupElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::option) + return adopt(*new HTML::HTMLOptionElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::output) + return adopt(*new HTML::HTMLOutputElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::p) + return adopt(*new HTML::HTMLParagraphElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::param) + return adopt(*new HTML::HTMLParamElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::picture) + return adopt(*new HTML::HTMLPictureElement(document, qualified_name)); + // NOTE: The obsolete elements "listing" and "xmp" are explicitly mapped to HTMLPreElement in the specification. + if (lowercase_tag_name.is_one_of(HTML::TagNames::pre, HTML::TagNames::listing, HTML::TagNames::xmp)) + return adopt(*new HTML::HTMLPreElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::progress) + return adopt(*new HTML::HTMLProgressElement(document, qualified_name)); + if (lowercase_tag_name.is_one_of(HTML::TagNames::blockquote, HTML::TagNames::q)) + return adopt(*new HTML::HTMLQuoteElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::script) + return adopt(*new HTML::HTMLScriptElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::select) + return adopt(*new HTML::HTMLSelectElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::slot) + return adopt(*new HTML::HTMLSlotElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::source) + return adopt(*new HTML::HTMLSourceElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::span) + return adopt(*new HTML::HTMLSpanElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::style) + return adopt(*new HTML::HTMLStyleElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::caption) + return adopt(*new HTML::HTMLTableCaptionElement(document, qualified_name)); + if (lowercase_tag_name.is_one_of(Web::HTML::TagNames::td, Web::HTML::TagNames::th)) + return adopt(*new HTML::HTMLTableCellElement(document, qualified_name)); + if (lowercase_tag_name.is_one_of(HTML::TagNames::colgroup, HTML::TagNames::col)) + return adopt(*new HTML::HTMLTableColElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::table) + return adopt(*new HTML::HTMLTableElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::tr) + return adopt(*new HTML::HTMLTableRowElement(document, qualified_name)); + if (lowercase_tag_name.is_one_of(HTML::TagNames::tbody, HTML::TagNames::thead, HTML::TagNames::tfoot)) + return adopt(*new HTML::HTMLTableSectionElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::template_) + return adopt(*new HTML::HTMLTemplateElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::textarea) + return adopt(*new HTML::HTMLTextAreaElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::time) + return adopt(*new HTML::HTMLTimeElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::title) + return adopt(*new HTML::HTMLTitleElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::track) + return adopt(*new HTML::HTMLTrackElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::ul) + return adopt(*new HTML::HTMLUListElement(document, qualified_name)); + if (lowercase_tag_name == HTML::TagNames::video) + return adopt(*new HTML::HTMLVideoElement(document, qualified_name)); + if (lowercase_tag_name.is_one_of( + HTML::TagNames::article, HTML::TagNames::section, HTML::TagNames::nav, HTML::TagNames::aside, HTML::TagNames::hgroup, HTML::TagNames::header, HTML::TagNames::footer, HTML::TagNames::address, HTML::TagNames::dt, HTML::TagNames::dd, HTML::TagNames::figure, HTML::TagNames::figcaption, HTML::TagNames::main, HTML::TagNames::em, HTML::TagNames::strong, HTML::TagNames::small, HTML::TagNames::s, HTML::TagNames::cite, HTML::TagNames::dfn, HTML::TagNames::abbr, HTML::TagNames::ruby, HTML::TagNames::rt, HTML::TagNames::rp, HTML::TagNames::code, HTML::TagNames::var, HTML::TagNames::samp, HTML::TagNames::kbd, HTML::TagNames::sub, HTML::TagNames::sup, HTML::TagNames::i, HTML::TagNames::b, HTML::TagNames::u, HTML::TagNames::mark, HTML::TagNames::bdi, HTML::TagNames::bdo, HTML::TagNames::wbr, HTML::TagNames::summary, HTML::TagNames::noscript, + // Obsolete + HTML::TagNames::acronym, HTML::TagNames::basefont, HTML::TagNames::big, HTML::TagNames::center, HTML::TagNames::nobr, HTML::TagNames::noembed, HTML::TagNames::noframes, HTML::TagNames::plaintext, HTML::TagNames::rb, HTML::TagNames::rtc, HTML::TagNames::strike, HTML::TagNames::tt)) + return adopt(*new HTML::HTMLElement(document, qualified_name)); + if (lowercase_tag_name == SVG::TagNames::svg) + return adopt(*new SVG::SVGSVGElement(document, qualified_name)); + if (lowercase_tag_name == SVG::TagNames::path) + return adopt(*new SVG::SVGPathElement(document, qualified_name)); + + // FIXME: If name is a valid custom element name, then return HTMLElement. + + return adopt(*new HTML::HTMLUnknownElement(document, qualified_name)); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/ElementFactory.h b/Userland/Libraries/LibWeb/DOM/ElementFactory.h new file mode 100644 index 0000000000..c1c811a9a4 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/ElementFactory.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-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/DOM/Element.h> + +namespace Web::DOM { + +NonnullRefPtr<Element> create_element(Document&, const FlyString& tag_name, const FlyString& namespace_); + +} diff --git a/Userland/Libraries/LibWeb/DOM/Event.cpp b/Userland/Libraries/LibWeb/DOM/Event.cpp new file mode 100644 index 0000000000..9fd75ab226 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Event.cpp @@ -0,0 +1,59 @@ +/* + * 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 <AK/Assertions.h> +#include <AK/TypeCasts.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/DOM/Node.h> +#include <LibWeb/DOM/ShadowRoot.h> + +namespace Web::DOM { + +void Event::append_to_path(EventTarget& invocation_target, RefPtr<EventTarget> shadow_adjusted_target, RefPtr<EventTarget> related_target, TouchTargetList& touch_targets, bool slot_in_closed_tree) +{ + bool invocation_target_in_shadow_tree = false; + bool root_of_closed_tree = false; + + if (is<Node>(invocation_target)) { + auto& invocation_target_node = downcast<Node>(invocation_target); + if (is<ShadowRoot>(invocation_target_node.root())) + invocation_target_in_shadow_tree = true; + if (is<ShadowRoot>(invocation_target_node)) { + auto& invocation_target_shadow_root = downcast<ShadowRoot>(invocation_target_node); + root_of_closed_tree = invocation_target_shadow_root.closed(); + } + } + + m_path.append({ invocation_target, invocation_target_in_shadow_tree, shadow_adjusted_target, related_target, touch_targets, root_of_closed_tree, slot_in_closed_tree, m_path.size() }); +} + +void Event::set_cancelled_flag() +{ + if (m_cancelable && !m_in_passive_listener) + m_cancelled = true; +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/Event.h b/Userland/Libraries/LibWeb/DOM/Event.h new file mode 100644 index 0000000000..1acc60a386 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Event.h @@ -0,0 +1,186 @@ +/* + * 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 <AK/FlyString.h> +#include <LibWeb/Bindings/Wrappable.h> +#include <LibWeb/DOM/EventTarget.h> + +namespace Web::DOM { + +class Event + : public RefCounted<Event> + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::EventWrapper; + + enum Phase : u16 { + None = 0, + CapturingPhase = 1, + AtTarget = 2, + BubblingPhase = 3, + }; + + using TouchTargetList = Vector<RefPtr<EventTarget>>; + + struct PathEntry { + RefPtr<EventTarget> invocation_target; + bool invocation_target_in_shadow_tree { false }; + RefPtr<EventTarget> shadow_adjusted_target; + RefPtr<EventTarget> related_target; + TouchTargetList touch_target_list; + bool root_of_closed_tree { false }; + bool slot_in_closed_tree { false }; + size_t index; + }; + + using Path = Vector<PathEntry>; + + static NonnullRefPtr<Event> create(const FlyString& event_name) + { + return adopt(*new Event(event_name)); + } + + virtual ~Event() { } + + const FlyString& type() const { return m_type; } + void set_type(const StringView& type) { m_type = type; } + + RefPtr<EventTarget> target() const { return m_target; } + void set_target(EventTarget* target) { m_target = target; } + + // NOTE: This is intended for the JS bindings. + RefPtr<EventTarget> src_target() const { return target(); } + + RefPtr<EventTarget> related_target() const { return m_related_target; } + void set_related_target(EventTarget* related_target) { m_related_target = related_target; } + + bool should_stop_propagation() const { return m_stop_propagation; } + void set_stop_propagation(bool stop_propagation) { m_stop_propagation = stop_propagation; } + + bool should_stop_immediate_propagation() const { return m_stop_immediate_propagation; } + void set_stop_immediate_propagation(bool stop_immediate_propagation) { m_stop_immediate_propagation = stop_immediate_propagation; } + + bool cancelled() const { return m_cancelled; } + void set_cancelled(bool cancelled) { m_cancelled = cancelled; } + + bool in_passive_listener() const { return m_in_passive_listener; } + void set_in_passive_listener(bool in_passive_listener) { m_in_passive_listener = in_passive_listener; } + + bool composed() const { return m_composed; } + void set_composed(bool composed) { m_composed = composed; } + + bool initialized() const { return m_initialized; } + void set_initialized(bool initialized) { m_initialized = initialized; } + + bool dispatched() const { return m_dispatch; } + void set_dispatched(bool dispatched) { m_dispatch = dispatched; } + + void prevent_default() { set_cancelled_flag(); } + bool default_prevented() const { return cancelled(); } + + u16 event_phase() const { return m_phase; } + void set_phase(Phase phase) { m_phase = phase; } + + RefPtr<EventTarget> current_target() const { return m_current_target; } + void set_current_target(EventTarget* current_target) { m_current_target = current_target; } + + bool return_value() const { return !m_cancelled; } + void set_return_value(bool return_value) + { + if (!return_value) + set_cancelled_flag(); + } + + void append_to_path(EventTarget&, RefPtr<EventTarget>, RefPtr<EventTarget>, TouchTargetList&, bool); + Path& path() { return m_path; } + const Path& path() const { return m_path; } + void clear_path() { m_path.clear(); } + + void set_touch_target_list(TouchTargetList& touch_target_list) { m_touch_target_list = touch_target_list; } + TouchTargetList& touch_target_list() { return m_touch_target_list; }; + void clear_touch_target_list() { m_touch_target_list.clear(); } + + bool bubbles() const { return m_bubbles; } + void set_bubbles(bool bubbles) { m_bubbles = bubbles; } + + bool cancelable() const { return m_cancelable; } + void set_cancelable(bool cancelable) { m_cancelable = cancelable; } + + bool is_trusted() const { return m_is_trusted; } + void set_is_trusted(bool is_trusted) { m_is_trusted = is_trusted; } + + void stop_propagation() { m_stop_propagation = true; } + + bool cancel_bubble() const { return m_stop_propagation; } + void set_cancel_bubble(bool cancel_bubble) + { + if (cancel_bubble) + m_stop_propagation = true; + } + + void stop_immediate_propagation() + { + m_stop_propagation = true; + m_stop_immediate_propagation = true; + } + +protected: + explicit Event(const FlyString& type) + : m_type(type) + , m_initialized(true) + { + } + +private: + FlyString m_type; + RefPtr<EventTarget> m_target; + RefPtr<EventTarget> m_related_target; + RefPtr<EventTarget> m_current_target; + + Phase m_phase { None }; + + bool m_bubbles { false }; + bool m_cancelable { false }; + + bool m_stop_propagation { false }; + bool m_stop_immediate_propagation { false }; + bool m_cancelled { false }; + bool m_in_passive_listener { false }; + bool m_composed { false }; + bool m_initialized { false }; + bool m_dispatch { false }; + + bool m_is_trusted { true }; + + Path m_path; + TouchTargetList m_touch_target_list; + + void set_cancelled_flag(); +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/Event.idl b/Userland/Libraries/LibWeb/DOM/Event.idl new file mode 100644 index 0000000000..a577092cae --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Event.idl @@ -0,0 +1,23 @@ +interface Event { + + readonly attribute DOMString type; + readonly attribute EventTarget? target; + readonly attribute EventTarget? srcTarget; + readonly attribute EventTarget? currentTarget; + + readonly attribute unsigned short eventPhase; + + undefined stopPropagation(); + attribute boolean cancelBubble; + undefined stopImmediatePropagation(); + + readonly attribute boolean bubbles; + readonly attribute boolean cancelable; + attribute boolean returnValue; + undefined preventDefault(); + readonly attribute boolean defaultPrevented; + readonly attribute boolean composed; + + readonly attribute boolean isTrusted; + +}; diff --git a/Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp b/Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp new file mode 100644 index 0000000000..e45b42c23e --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp @@ -0,0 +1,332 @@ +/* + * 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/Assertions.h> +#include <AK/TypeCasts.h> +#include <LibJS/Runtime/Function.h> +#include <LibWeb/Bindings/EventTargetWrapper.h> +#include <LibWeb/Bindings/EventTargetWrapperFactory.h> +#include <LibWeb/Bindings/EventWrapper.h> +#include <LibWeb/Bindings/EventWrapperFactory.h> +#include <LibWeb/Bindings/ScriptExecutionContext.h> +#include <LibWeb/Bindings/WindowObject.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/DOM/EventDispatcher.h> +#include <LibWeb/DOM/EventListener.h> +#include <LibWeb/DOM/EventTarget.h> +#include <LibWeb/DOM/Node.h> +#include <LibWeb/DOM/ShadowRoot.h> +#include <LibWeb/DOM/Window.h> +#include <LibWeb/HTML/EventNames.h> +#include <LibWeb/UIEvents/MouseEvent.h> + +namespace Web::DOM { + +// FIXME: This shouldn't be here, as retargeting is not only used by the event dispatcher. +// When moving this function, it needs to be generalized. https://dom.spec.whatwg.org/#retarget +static EventTarget* retarget(EventTarget* left, [[maybe_unused]] EventTarget* right) +{ + // FIXME + for (;;) { + if (!is<Node>(left)) + return left; + + auto* left_node = downcast<Node>(left); + auto* left_root = left_node->root(); + if (!is<ShadowRoot>(left_root)) + return left; + + // FIXME: If right is a node and left’s root is a shadow-including inclusive ancestor of right, return left. + + auto* left_shadow_root = downcast<ShadowRoot>(left_root); + left = left_shadow_root->host(); + } +} + +// https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke +bool EventDispatcher::inner_invoke(Event& event, Vector<EventTarget::EventListenerRegistration>& listeners, Event::Phase phase, bool invocation_target_in_shadow_tree) +{ + bool found = false; + + for (auto& listener : listeners) { + if (listener.listener->removed()) + continue; + + if (event.type() != listener.listener->type()) + continue; + + found = true; + + if (phase == Event::Phase::CapturingPhase && !listener.listener->capture()) + continue; + + if (phase == Event::Phase::BubblingPhase && listener.listener->capture()) + continue; + + if (listener.listener->once()) + event.current_target()->remove_from_event_listener_list(listener.listener); + + auto& function = listener.listener->function(); + auto& global = function.global_object(); + + RefPtr<Event> current_event; + + if (is<Bindings::WindowObject>(global)) { + auto& bindings_window_global = downcast<Bindings::WindowObject>(global); + auto& window_impl = bindings_window_global.impl(); + current_event = window_impl.current_event(); + if (!invocation_target_in_shadow_tree) + window_impl.set_current_event(&event); + } + + if (listener.listener->passive()) + event.set_in_passive_listener(true); + + auto* this_value = Bindings::wrap(global, *event.current_target()); + auto* wrapped_event = Bindings::wrap(global, event); + auto& vm = global.vm(); + [[maybe_unused]] auto rc = vm.call(listener.listener->function(), this_value, wrapped_event); + if (vm.exception()) { + vm.clear_exception(); + // FIXME: Set legacyOutputDidListenersThrowFlag if given. (Only used by IndexedDB currently) + } + + event.set_in_passive_listener(false); + if (is<Bindings::WindowObject>(global)) { + auto& bindings_window_global = downcast<Bindings::WindowObject>(global); + auto& window_impl = bindings_window_global.impl(); + window_impl.set_current_event(current_event); + } + + if (event.should_stop_immediate_propagation()) + return found; + } + + return found; +} + +// https://dom.spec.whatwg.org/#concept-event-listener-invoke +void EventDispatcher::invoke(Event::PathEntry& struct_, Event& event, Event::Phase phase) +{ + auto last_valid_shadow_adjusted_target = event.path().last_matching([&struct_](auto& entry) { + return entry.index <= struct_.index && !entry.shadow_adjusted_target.is_null(); + }); + + ASSERT(last_valid_shadow_adjusted_target.has_value()); + + event.set_target(last_valid_shadow_adjusted_target.value().shadow_adjusted_target); + event.set_related_target(struct_.related_target); + event.set_touch_target_list(struct_.touch_target_list); + + if (event.should_stop_propagation()) + return; + + event.set_current_target(struct_.invocation_target); + + // NOTE: This is an intentional copy. Any event listeners added after this point will not be invoked. + auto listeners = event.current_target()->listeners(); + bool invocation_target_in_shadow_tree = struct_.invocation_target_in_shadow_tree; + + bool found = inner_invoke(event, listeners, phase, invocation_target_in_shadow_tree); + + if (!found && event.is_trusted()) { + auto original_event_type = event.type(); + + if (event.type() == "animationend") + event.set_type("webkitAnimationEnd"); + else if (event.type() == "animationiteration") + event.set_type("webkitAnimationIteration"); + else if (event.type() == "animationstart") + event.set_type("webkitAnimationStart"); + else if (event.type() == "transitionend") + event.set_type("webkitTransitionEnd"); + else + return; + + inner_invoke(event, listeners, phase, invocation_target_in_shadow_tree); + event.set_type(original_event_type); + } +} + +// https://dom.spec.whatwg.org/#concept-event-dispatch +bool EventDispatcher::dispatch(NonnullRefPtr<EventTarget> target, NonnullRefPtr<Event> event, bool legacy_target_override) +{ + event->set_dispatched(true); + RefPtr<EventTarget> target_override; + + if (!legacy_target_override) { + target_override = target; + } else { + // NOTE: This can be done because legacy_target_override is only set for events targeted at Window. + target_override = downcast<Window>(*target).document(); + } + + RefPtr<EventTarget> activation_target; + RefPtr<EventTarget> related_target = retarget(event->related_target(), target); + + bool clear_targets = false; + + if (related_target != target || event->related_target() == target) { + Event::TouchTargetList touch_targets; + + for (auto& touch_target : event->touch_target_list()) { + touch_targets.append(retarget(touch_target, target)); + } + + event->append_to_path(*target, target_override, related_target, touch_targets, false); + + bool is_activation_event = is<UIEvents::MouseEvent>(*event) && event->type() == HTML::EventNames::click; + + if (is_activation_event && target->activation_behaviour) + activation_target = target; + + // FIXME: Let slottable be target, if target is a slottable and is assigned, and null otherwise. + + bool slot_in_closed_tree = false; + auto* parent = target->get_parent(event); + + while (parent) { + // FIXME: If slottable is non-null: + + // FIXME: If parent is a slottable and is assigned, then set slottable to parent. + + related_target = retarget(event->related_target(), parent); + touch_targets.clear(); + + for (auto& touch_target : event->touch_target_list()) { + touch_targets.append(retarget(touch_target, parent)); + } + + // FIXME: or parent is a node and target’s root is a shadow-including inclusive ancestor of parent, then: + if (is<Window>(parent)) { + if (is_activation_event && event->bubbles() && !activation_target && parent->activation_behaviour) + activation_target = parent; + + event->append_to_path(*parent, nullptr, related_target, touch_targets, slot_in_closed_tree); + } else if (related_target == parent) { + parent = nullptr; + } else { + target = *parent; + + if (is_activation_event && !activation_target && target->activation_behaviour) + activation_target = target; + + event->append_to_path(*parent, target, related_target, touch_targets, slot_in_closed_tree); + } + + if (parent) { + parent = parent->get_parent(event); + } + + slot_in_closed_tree = false; + } + + auto clear_targets_struct = event->path().last_matching([](auto& entry) { + return !entry.shadow_adjusted_target.is_null(); + }); + + ASSERT(clear_targets_struct.has_value()); + + if (is<Node>(clear_targets_struct.value().shadow_adjusted_target.ptr())) { + auto& shadow_adjusted_target_node = downcast<Node>(*clear_targets_struct.value().shadow_adjusted_target); + if (is<ShadowRoot>(shadow_adjusted_target_node.root())) + clear_targets = true; + } + + if (!clear_targets && is<Node>(clear_targets_struct.value().related_target.ptr())) { + auto& related_target_node = downcast<Node>(*clear_targets_struct.value().related_target); + if (is<ShadowRoot>(related_target_node.root())) + clear_targets = true; + } + + if (!clear_targets) { + for (auto touch_target : clear_targets_struct.value().touch_target_list) { + if (is<Node>(*touch_target.ptr())) { + auto& touch_target_node = downcast<Node>(*touch_target.ptr()); + if (is<ShadowRoot>(touch_target_node.root())) { + clear_targets = true; + break; + } + } + } + } + + if (activation_target && activation_target->legacy_pre_activation_behaviour) + activation_target->legacy_pre_activation_behaviour(); + + for (ssize_t i = event->path().size() - 1; i >= 0; --i) { + auto& entry = event->path().at(i); + + if (entry.shadow_adjusted_target) + event->set_phase(Event::Phase::AtTarget); + else + event->set_phase(Event::Phase::CapturingPhase); + + invoke(entry, event, Event::Phase::CapturingPhase); + } + + for (auto& entry : event->path()) { + if (entry.shadow_adjusted_target) { + event->set_phase(Event::Phase::AtTarget); + } else { + if (!event->bubbles()) + continue; + + event->set_phase(Event::Phase::BubblingPhase); + } + + invoke(entry, event, Event::Phase::BubblingPhase); + } + } + + event->set_phase(Event::Phase::None); + event->set_current_target(nullptr); + event->clear_path(); + event->set_dispatched(false); + event->set_stop_propagation(false); + event->set_stop_immediate_propagation(false); + + if (clear_targets) { + event->set_target(nullptr); + event->set_related_target(nullptr); + event->clear_touch_target_list(); + } + + if (activation_target) { + if (!event->cancelled()) { + // NOTE: Since activation_target is set, it will have activation behaviour. + activation_target->activation_behaviour(event); + } else { + if (activation_target->legacy_cancelled_activation_behaviour) + activation_target->legacy_cancelled_activation_behaviour(); + } + } + + return !event->cancelled(); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/EventDispatcher.h b/Userland/Libraries/LibWeb/DOM/EventDispatcher.h new file mode 100644 index 0000000000..c5c380c597 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/EventDispatcher.h @@ -0,0 +1,43 @@ +/* + * 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 <AK/Forward.h> +#include <LibWeb/Forward.h> + +namespace Web::DOM { + +class EventDispatcher { +public: + static bool dispatch(NonnullRefPtr<EventTarget>, NonnullRefPtr<Event>, bool legacy_target_override = false); + +private: + static void invoke(Event::PathEntry&, Event&, Event::Phase); + static bool inner_invoke(Event&, Vector<EventTarget::EventListenerRegistration>&, Event::Phase, bool); +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/EventListener.cpp b/Userland/Libraries/LibWeb/DOM/EventListener.cpp new file mode 100644 index 0000000000..eb64bfa503 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/EventListener.cpp @@ -0,0 +1,38 @@ +/* + * 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 <LibJS/Runtime/Function.h> +#include <LibWeb/DOM/EventListener.h> + +namespace Web::DOM { + +JS::Function& EventListener::function() +{ + ASSERT(m_function.cell()); + return *m_function.cell(); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/EventListener.h b/Userland/Libraries/LibWeb/DOM/EventListener.h new file mode 100644 index 0000000000..1d5705c2b6 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/EventListener.h @@ -0,0 +1,72 @@ +/* + * 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 <AK/RefCounted.h> +#include <LibJS/Heap/Handle.h> +#include <LibWeb/Bindings/Wrappable.h> + +namespace Web::DOM { + +class EventListener + : public RefCounted<EventListener> + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::EventListenerWrapper; + + explicit EventListener(JS::Handle<JS::Function> function) + : m_function(move(function)) + { + } + + JS::Function& function(); + + const FlyString& type() const { return m_type; } + void set_type(const FlyString& type) { m_type = type; } + + bool capture() const { return m_capture; } + void set_capture(bool capture) { m_capture = capture; } + + bool passive() const { return m_passive; } + void set_passive(bool passive) { m_capture = passive; } + + bool once() const { return m_once; } + void set_once(bool once) { m_once = once; } + + bool removed() const { return m_removed; } + void set_removed(bool removed) { m_removed = removed; } + +private: + FlyString m_type; + JS::Handle<JS::Function> m_function; + bool m_capture { false }; + bool m_passive { false }; + bool m_once { false }; + bool m_removed { false }; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/EventTarget.cpp b/Userland/Libraries/LibWeb/DOM/EventTarget.cpp new file mode 100644 index 0000000000..5f8ca71c8a --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/EventTarget.cpp @@ -0,0 +1,70 @@ +/* + * 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 <LibWeb/Bindings/ScriptExecutionContext.h> +#include <LibWeb/DOM/EventListener.h> +#include <LibWeb/DOM/EventTarget.h> + +namespace Web::DOM { + +EventTarget::EventTarget(Bindings::ScriptExecutionContext& script_execution_context) + : m_script_execution_context(&script_execution_context) +{ +} + +EventTarget::~EventTarget() +{ +} + +void EventTarget::add_event_listener(const FlyString& event_name, NonnullRefPtr<EventListener> listener) +{ + auto existing_listener = m_listeners.first_matching([&](auto& entry) { + return entry.listener->type() == event_name && &entry.listener->function() == &listener->function() && entry.listener->capture() == listener->capture(); + }); + if (existing_listener.has_value()) + return; + listener->set_type(event_name); + m_listeners.append({ event_name, move(listener) }); +} + +void EventTarget::remove_event_listener(const FlyString& event_name, NonnullRefPtr<EventListener> listener) +{ + m_listeners.remove_first_matching([&](auto& entry) { + auto matches = entry.event_name == event_name && &entry.listener->function() == &listener->function() && entry.listener->capture() == listener->capture(); + if (matches) + entry.listener->set_removed(true); + return matches; + }); +} + +void EventTarget::remove_from_event_listener_list(NonnullRefPtr<EventListener> listener) +{ + m_listeners.remove_first_matching([&](auto& entry) { + return entry.listener->type() == listener->type() && &entry.listener->function() == &listener->function() && entry.listener->capture() == listener->capture(); + }); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/EventTarget.h b/Userland/Libraries/LibWeb/DOM/EventTarget.h new file mode 100644 index 0000000000..32399a6a7a --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/EventTarget.h @@ -0,0 +1,85 @@ +/* + * 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 <AK/FlyString.h> +#include <AK/Function.h> +#include <AK/Noncopyable.h> +#include <AK/Vector.h> +#include <LibJS/Forward.h> +#include <LibWeb/Forward.h> + +namespace Web::DOM { + +class EventTarget { + AK_MAKE_NONCOPYABLE(EventTarget); + AK_MAKE_NONMOVABLE(EventTarget); + +public: + virtual ~EventTarget(); + + void ref() { ref_event_target(); } + void unref() { unref_event_target(); } + + void add_event_listener(const FlyString& event_name, NonnullRefPtr<EventListener>); + void remove_event_listener(const FlyString& event_name, NonnullRefPtr<EventListener>); + + void remove_from_event_listener_list(NonnullRefPtr<EventListener>); + + virtual bool dispatch_event(NonnullRefPtr<Event>) = 0; + virtual Bindings::EventTargetWrapper* create_wrapper(JS::GlobalObject&) = 0; + Bindings::ScriptExecutionContext* script_execution_context() { return m_script_execution_context; } + + virtual EventTarget* get_parent(const Event&) { return nullptr; } + + struct EventListenerRegistration { + FlyString event_name; + NonnullRefPtr<EventListener> listener; + }; + + const Vector<EventListenerRegistration>& listeners() const { return m_listeners; } + + Function<void(const Event&)> activation_behaviour; + + // NOTE: These only exist for checkbox and radio input elements. + Function<void()> legacy_pre_activation_behaviour; + Function<void()> legacy_cancelled_activation_behaviour; + +protected: + explicit EventTarget(Bindings::ScriptExecutionContext&); + + virtual void ref_event_target() = 0; + virtual void unref_event_target() = 0; + +private: + // FIXME: This should not be a raw pointer. + Bindings::ScriptExecutionContext* m_script_execution_context { nullptr }; + + Vector<EventListenerRegistration> m_listeners; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/EventTarget.idl b/Userland/Libraries/LibWeb/DOM/EventTarget.idl new file mode 100644 index 0000000000..2dacfae0e2 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/EventTarget.idl @@ -0,0 +1,6 @@ +interface EventTarget { + + undefined addEventListener(DOMString type, EventListener? callback); + undefined removeEventListener(DOMString type, EventListener? callback); + +}; diff --git a/Userland/Libraries/LibWeb/DOM/Node.cpp b/Userland/Libraries/LibWeb/DOM/Node.cpp new file mode 100644 index 0000000000..6890e4fda5 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Node.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2018-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/StringBuilder.h> +#include <LibJS/AST.h> +#include <LibJS/Runtime/Function.h> +#include <LibJS/Runtime/ScriptFunction.h> +#include <LibWeb/Bindings/EventWrapper.h> +#include <LibWeb/Bindings/EventWrapperFactory.h> +#include <LibWeb/Bindings/NodeWrapper.h> +#include <LibWeb/Bindings/NodeWrapperFactory.h> +#include <LibWeb/CSS/StyleResolver.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/DOM/EventDispatcher.h> +#include <LibWeb/DOM/EventListener.h> +#include <LibWeb/DOM/Node.h> +#include <LibWeb/DOM/ShadowRoot.h> +#include <LibWeb/HTML/HTMLAnchorElement.h> +#include <LibWeb/Layout/BlockBox.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> +#include <LibWeb/Layout/InlineNode.h> +#include <LibWeb/Layout/Node.h> +#include <LibWeb/Layout/TextNode.h> + +//#define EVENT_DEBUG + +namespace Web::DOM { + +Node::Node(Document& document, NodeType type) + : EventTarget(static_cast<Bindings::ScriptExecutionContext&>(document)) + , m_document(&document) + , m_type(type) +{ + if (!is_document()) + m_document->ref_from_node({}); +} + +Node::~Node() +{ + ASSERT(m_deletion_has_begun); + if (layout_node() && layout_node()->parent()) + layout_node()->parent()->remove_child(*layout_node()); + + if (!is_document()) + m_document->unref_from_node({}); +} + +const HTML::HTMLAnchorElement* Node::enclosing_link_element() const +{ + for (auto* node = this; node; node = node->parent()) { + if (is<HTML::HTMLAnchorElement>(*node) && downcast<HTML::HTMLAnchorElement>(*node).has_attribute(HTML::AttributeNames::href)) + return downcast<HTML::HTMLAnchorElement>(node); + } + return nullptr; +} + +const HTML::HTMLElement* Node::enclosing_html_element() const +{ + return first_ancestor_of_type<HTML::HTMLElement>(); +} + +String Node::text_content() const +{ + StringBuilder builder; + for (auto* child = first_child(); child; child = child->next_sibling()) { + builder.append(child->text_content()); + } + return builder.to_string(); +} + +void Node::set_text_content(const String& content) +{ + if (is_text()) { + downcast<Text>(this)->set_data(content); + } else { + remove_all_children(); + append_child(document().create_text_node(content)); + } + + set_needs_style_update(true); + document().invalidate_layout(); +} + +RefPtr<Layout::Node> Node::create_layout_node() +{ + return nullptr; +} + +void Node::invalidate_style() +{ + for_each_in_subtree_of_type<Element>([&](auto& element) { + element.set_needs_style_update(true); + return IterationDecision::Continue; + }); + document().schedule_style_update(); +} + +bool Node::is_link() const +{ + return enclosing_link_element(); +} + +bool Node::dispatch_event(NonnullRefPtr<Event> event) +{ + return EventDispatcher::dispatch(*this, event); +} + +String Node::child_text_content() const +{ + if (!is<ParentNode>(*this)) + return String::empty(); + + StringBuilder builder; + downcast<ParentNode>(*this).for_each_child([&](auto& child) { + if (is<Text>(child)) + builder.append(downcast<Text>(child).text_content()); + }); + return builder.build(); +} + +Node* Node::root() +{ + Node* root = this; + while (root->parent()) + root = root->parent(); + return root; +} + +Node* Node::shadow_including_root() +{ + auto node_root = root(); + if (is<ShadowRoot>(node_root)) + return downcast<ShadowRoot>(node_root)->host()->shadow_including_root(); + return node_root; +} + +bool Node::is_connected() const +{ + return shadow_including_root() && shadow_including_root()->is_document(); +} + +Element* Node::parent_element() +{ + if (!parent() || !is<Element>(parent())) + return nullptr; + return downcast<Element>(parent()); +} + +const Element* Node::parent_element() const +{ + if (!parent() || !is<Element>(parent())) + return nullptr; + return downcast<Element>(parent()); +} + +RefPtr<Node> Node::append_child(NonnullRefPtr<Node> node, bool notify) +{ + if (&node->document() != &document()) + document().adopt_node(node); + TreeNode<Node>::append_child(node, notify); + return node; +} + +RefPtr<Node> Node::insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool notify) +{ + if (!child) + return append_child(move(node), notify); + if (child->parent_node() != this) { + dbgln("FIXME: Trying to insert_before() a bogus child"); + return nullptr; + } + if (&node->document() != &document()) + document().adopt_node(node); + TreeNode<Node>::insert_before(node, child, notify); + return node; +} + +void Node::set_document(Badge<Document>, Document& document) +{ + if (m_document == &document) + return; + document.ref_from_node({}); + m_document->unref_from_node({}); + m_document = &document; +} + +bool Node::is_editable() const +{ + return parent() && parent()->is_editable(); +} + +Bindings::EventTargetWrapper* Node::create_wrapper(JS::GlobalObject& global_object) +{ + return wrap(global_object, *this); +} + +void Node::removed_last_ref() +{ + if (is<Document>(*this)) { + downcast<Document>(*this).removed_last_ref(); + return; + } + m_deletion_has_begun = true; + delete this; +} + +void Node::set_layout_node(Badge<Layout::Node>, Layout::Node* layout_node) const +{ + if (layout_node) + m_layout_node = layout_node->make_weak_ptr(); + else + m_layout_node = nullptr; +} + +EventTarget* Node::get_parent(const Event&) +{ + // FIXME: returns the node’s assigned slot, if node is assigned, and node’s parent otherwise. + return parent(); +} + +void Node::set_needs_style_update(bool value) +{ + if (m_needs_style_update == value) + return; + m_needs_style_update = value; + + if (m_needs_style_update) { + for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) + ancestor->m_child_needs_style_update = true; + document().schedule_style_update(); + } +} + +void Node::inserted_into(Node&) +{ + set_needs_style_update(true); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/Node.h b/Userland/Libraries/LibWeb/DOM/Node.h new file mode 100644 index 0000000000..624b41b951 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Node.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2018-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 <AK/Badge.h> +#include <AK/RefPtr.h> +#include <AK/String.h> +#include <AK/TypeCasts.h> +#include <AK/Vector.h> +#include <LibWeb/Bindings/Wrappable.h> +#include <LibWeb/DOM/EventTarget.h> +#include <LibWeb/TreeNode.h> + +namespace Web::DOM { + +enum class NodeType : unsigned { + INVALID = 0, + ELEMENT_NODE = 1, + TEXT_NODE = 3, + COMMENT_NODE = 8, + DOCUMENT_NODE = 9, + DOCUMENT_TYPE_NODE = 10, + DOCUMENT_FRAGMENT_NODE = 11, +}; + +class Node + : public TreeNode<Node> + , public EventTarget + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::NodeWrapper; + + using TreeNode<Node>::ref; + using TreeNode<Node>::unref; + + // ^EventTarget + virtual void ref_event_target() final { ref(); } + virtual void unref_event_target() final { unref(); } + virtual bool dispatch_event(NonnullRefPtr<Event>) final; + virtual Bindings::EventTargetWrapper* create_wrapper(JS::GlobalObject&) override; + + virtual ~Node(); + + void removed_last_ref(); + + NodeType type() const { return m_type; } + bool is_element() const { return type() == NodeType::ELEMENT_NODE; } + bool is_text() const { return type() == NodeType::TEXT_NODE; } + bool is_document() const { return type() == NodeType::DOCUMENT_NODE; } + bool is_document_type() const { return type() == NodeType::DOCUMENT_TYPE_NODE; } + bool is_comment() const { return type() == NodeType::COMMENT_NODE; } + bool is_character_data() const { return type() == NodeType::TEXT_NODE || type() == NodeType::COMMENT_NODE; } + bool is_document_fragment() const { return type() == NodeType::DOCUMENT_FRAGMENT_NODE; } + bool is_parent_node() const { return is_element() || is_document() || is_document_fragment(); } + bool is_slottable() const { return is_element() || is_text(); } + + virtual bool is_editable() const; + + RefPtr<Node> append_child(NonnullRefPtr<Node>, bool notify = true); + RefPtr<Node> insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool notify = true); + + virtual RefPtr<Layout::Node> create_layout_node(); + + virtual FlyString node_name() const = 0; + + virtual String text_content() const; + void set_text_content(const String&); + + Document& document() { return *m_document; } + const Document& document() const { return *m_document; } + + const HTML::HTMLAnchorElement* enclosing_link_element() const; + const HTML::HTMLElement* enclosing_html_element() const; + + String child_text_content() const; + + Node* root(); + const Node* root() const + { + return const_cast<Node*>(this)->root(); + } + + Node* shadow_including_root(); + const Node* shadow_including_root() const + { + return const_cast<Node*>(this)->shadow_including_root(); + } + + bool is_connected() const; + + Node* parent_node() { return parent(); } + const Node* parent_node() const { return parent(); } + + Element* parent_element(); + const Element* parent_element() const; + + virtual void inserted_into(Node&); + virtual void removed_from(Node&) { } + virtual void children_changed() { } + + const Layout::Node* layout_node() const { return m_layout_node; } + Layout::Node* layout_node() { return m_layout_node; } + + void set_layout_node(Badge<Layout::Node>, Layout::Node*) const; + + virtual bool is_child_allowed(const Node&) const { return true; } + + bool needs_style_update() const { return m_needs_style_update; } + void set_needs_style_update(bool); + + bool child_needs_style_update() const { return m_child_needs_style_update; } + void set_child_needs_style_update(bool b) { m_child_needs_style_update = b; } + + void invalidate_style(); + + bool is_link() const; + + void set_document(Badge<Document>, Document&); + + virtual EventTarget* get_parent(const Event&) override; + +protected: + Node(Document&, NodeType); + + Document* m_document { nullptr }; + mutable WeakPtr<Layout::Node> m_layout_node; + NodeType m_type { NodeType::INVALID }; + bool m_needs_style_update { false }; + bool m_child_needs_style_update { false }; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/Node.idl b/Userland/Libraries/LibWeb/DOM/Node.idl new file mode 100644 index 0000000000..2ba27688e6 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Node.idl @@ -0,0 +1,16 @@ +interface Node : EventTarget { + + readonly attribute DOMString nodeName; + readonly attribute Node? firstChild; + readonly attribute Node? lastChild; + readonly attribute Node? previousSibling; + readonly attribute Node? nextSibling; + readonly attribute Node? parentNode; + readonly attribute Element? parentElement; + attribute DOMString textContent; + + Node appendChild(Node node); + Node insertBefore(Node node, Node? child); + +}; + diff --git a/Userland/Libraries/LibWeb/DOM/NonDocumentTypeChildNode.h b/Userland/Libraries/LibWeb/DOM/NonDocumentTypeChildNode.h new file mode 100644 index 0000000000..2e3b3996d9 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/NonDocumentTypeChildNode.h @@ -0,0 +1,73 @@ +/* + * 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 <AK/Forward.h> +#include <LibWeb/Forward.h> +#include <LibWeb/TreeNode.h> + +namespace Web::DOM { + +template<typename NodeType> +class NonDocumentTypeChildNode { +public: + Element* previous_element_sibling() + { + for (auto* sibling = static_cast<NodeType*>(this)->previous_sibling(); sibling; sibling = sibling->previous_sibling()) { + if (is<Element>(*sibling)) + return downcast<Element>(sibling); + } + return nullptr; + } + + Element* next_element_sibling() + { + for (auto* sibling = static_cast<NodeType*>(this)->next_sibling(); sibling; sibling = sibling->next_sibling()) { + if (is<Element>(*sibling)) + return downcast<Element>(sibling); + } + return nullptr; + } + + Element* next_element_in_pre_order() + { + for (auto* node = static_cast<NodeType*>(this)->next_in_pre_order(); node; node = node->next_in_pre_order()) { + if (is<Element>(*node)) + return downcast<Element>(node); + } + return nullptr; + } + + const Element* previous_element_sibling() const { return const_cast<NonDocumentTypeChildNode*>(this)->previous_element_sibling(); } + const Element* next_element_sibling() const { return const_cast<NonDocumentTypeChildNode*>(this)->next_element_sibling(); } + const Element* next_element_in_pre_order() const { return const_cast<NonDocumentTypeChildNode*>(this)->next_element_in_pre_order(); } + +protected: + NonDocumentTypeChildNode() { } +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/NonElementParentNode.h b/Userland/Libraries/LibWeb/DOM/NonElementParentNode.h new file mode 100644 index 0000000000..e362fbc5c0 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/NonElementParentNode.h @@ -0,0 +1,60 @@ +/* + * 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 <AK/Forward.h> +#include <LibWeb/Forward.h> +#include <LibWeb/HTML/AttributeNames.h> +#include <LibWeb/TreeNode.h> + +namespace Web::DOM { + +template<typename NodeType> +class NonElementParentNode { +public: + RefPtr<Element> get_element_by_id(const FlyString& id) const + { + RefPtr<Element> found_element; + static_cast<const NodeType*>(this)->template for_each_in_subtree_of_type<Element>([&](auto& element) { + if (element.attribute(HTML::AttributeNames::id) == id) { + found_element = &element; + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + return found_element; + } + RefPtr<Element> get_element_by_id(const FlyString& id) + { + return const_cast<const NonElementParentNode*>(this)->get_element_by_id(id); + } + +protected: + NonElementParentNode() { } +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/ParentNode.cpp b/Userland/Libraries/LibWeb/DOM/ParentNode.cpp new file mode 100644 index 0000000000..42f435c19e --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/ParentNode.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2020, Luke Wilde <luke.wilde@live.co.uk> + * 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 <LibWeb/CSS/Parser/CSSParser.h> +#include <LibWeb/CSS/SelectorEngine.h> +#include <LibWeb/DOM/ParentNode.h> +#include <LibWeb/Dump.h> + +namespace Web::DOM { + +RefPtr<Element> ParentNode::query_selector(const StringView& selector_text) +{ + auto selector = parse_selector(CSS::ParsingContext(*this), selector_text); + if (!selector.has_value()) + return {}; + + dump_selector(selector.value()); + + RefPtr<Element> result; + for_each_in_subtree_of_type<Element>([&](auto& element) { + if (SelectorEngine::matches(selector.value(), element)) { + result = element; + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + + return result; +} + +NonnullRefPtrVector<Element> ParentNode::query_selector_all(const StringView& selector_text) +{ + auto selector = parse_selector(CSS::ParsingContext(*this), selector_text); + if (!selector.has_value()) + return {}; + + dump_selector(selector.value()); + + NonnullRefPtrVector<Element> elements; + for_each_in_subtree_of_type<Element>([&](auto& element) { + if (SelectorEngine::matches(selector.value(), element)) { + elements.append(element); + } + return IterationDecision::Continue; + }); + + return elements; +} + +RefPtr<Element> ParentNode::first_element_child() +{ + return first_child_of_type<Element>(); +} + +RefPtr<Element> ParentNode::last_element_child() +{ + return last_child_of_type<Element>(); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/ParentNode.h b/Userland/Libraries/LibWeb/DOM/ParentNode.h new file mode 100644 index 0000000000..0fa6e8f1fe --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/ParentNode.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018-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 <AK/NonnullRefPtrVector.h> +#include <LibWeb/DOM/Node.h> + +namespace Web::DOM { + +class ParentNode : public Node { +public: + template<typename F> + void for_each_child(F) const; + template<typename F> + void for_each_child(F); + + RefPtr<Element> first_element_child(); + RefPtr<Element> last_element_child(); + + RefPtr<Element> query_selector(const StringView&); + NonnullRefPtrVector<Element> query_selector_all(const StringView&); + +protected: + ParentNode(Document& document, NodeType type) + : Node(document, type) + { + } +}; + +template<typename Callback> +inline void ParentNode::for_each_child(Callback callback) const +{ + for (auto* node = first_child(); node; node = node->next_sibling()) + callback(*node); +} + +template<typename Callback> +inline void ParentNode::for_each_child(Callback callback) +{ + for (auto* node = first_child(); node; node = node->next_sibling()) + callback(*node); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/Position.cpp b/Userland/Libraries/LibWeb/DOM/Position.cpp new file mode 100644 index 0000000000..e78139f788 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Position.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-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 <LibWeb/DOM/Node.h> +#include <LibWeb/DOM/Position.h> + +namespace Web::DOM { + +Position::Position(Node& node, unsigned offset) + : m_node(node) + , m_offset(offset) +{ +} + +Position::~Position() +{ +} + +String Position::to_string() const +{ + if (!node()) + return String::formatted("DOM::Position(nullptr, {})", offset()); + return String::formatted("DOM::Position({} ({})), {})", node()->node_name(), node(), offset()); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/Position.h b/Userland/Libraries/LibWeb/DOM/Position.h new file mode 100644 index 0000000000..b2c5a17ceb --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Position.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018-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 <AK/RefPtr.h> +#include <LibWeb/DOM/Node.h> +#include <LibWeb/Forward.h> + +namespace Web::DOM { + +class Position { +public: + Position() { } + Position(Node&, unsigned offset); + + ~Position(); + + bool is_valid() const { return m_node; } + + Node* node() { return m_node; } + const Node* node() const { return m_node; } + + unsigned offset() const { return m_offset; } + void set_offset(unsigned value) { m_offset = value; } + + bool operator==(const Position& other) const + { + return m_node == other.m_node && m_offset == other.m_offset; + } + + bool operator!=(const Position& other) const + { + return !(*this == other); + } + + String to_string() const; + +private: + RefPtr<Node> m_node; + unsigned m_offset { 0 }; +}; + +} + +namespace AK { +template<> +struct Formatter<Web::DOM::Position> : Formatter<StringView> { + void format(FormatBuilder& builder, const Web::DOM::Position& value) + { + Formatter<StringView>::format(builder, value.to_string()); + } +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/Range.cpp b/Userland/Libraries/LibWeb/DOM/Range.cpp new file mode 100644 index 0000000000..377eb7f882 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Range.cpp @@ -0,0 +1,75 @@ +/* + * 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 <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Node.h> +#include <LibWeb/DOM/Range.h> +#include <LibWeb/DOM/Window.h> + +namespace Web::DOM { + +Range::Range(Window& window) + : m_start_container(window.document()) + , m_start_offset(0) + , m_end_container(window.document()) + , m_end_offset(0) +{ +} + +Range::Range(Node& start_container, size_t start_offset, Node& end_container, size_t end_offset) + : m_start_container(start_container) + , m_start_offset(start_offset) + , m_end_container(end_container) + , m_end_offset(end_offset) +{ +} + +NonnullRefPtr<Range> Range::clone_range() const +{ + return adopt(*new Range(const_cast<Node&>(*m_start_container), m_start_offset, const_cast<Node&>(*m_end_container), m_end_offset)); +} + +NonnullRefPtr<Range> Range::inverted() const +{ + return adopt(*new Range(const_cast<Node&>(*m_end_container), m_end_offset, const_cast<Node&>(*m_start_container), m_start_offset)); +} + +NonnullRefPtr<Range> Range::normalized() const +{ + if (m_start_container.ptr() == m_end_container.ptr()) { + if (m_start_offset <= m_end_offset) + return clone_range(); + + return inverted(); + } + + if (m_start_container->is_before(m_end_container)) + return clone_range(); + + return inverted(); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/Range.h b/Userland/Libraries/LibWeb/DOM/Range.h new file mode 100644 index 0000000000..608112fb14 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Range.h @@ -0,0 +1,90 @@ +/* + * 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 <AK/RefCounted.h> +#include <LibWeb/Bindings/Wrappable.h> +#include <LibWeb/DOM/Node.h> + +namespace Web::DOM { + +class Range final + : public RefCounted<Range> + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::RangeWrapper; + + static NonnullRefPtr<Range> create(Window& window) + { + return adopt(*new Range(window)); + } + static NonnullRefPtr<Range> create(Node& start_container, size_t start_offset, Node& end_container, size_t end_offset) + { + return adopt(*new Range(start_container, start_offset, end_container, end_offset)); + } + + // FIXME: There are a ton of methods missing here. + + Node* start_container() { return m_start_container; } + unsigned start_offset() { return m_start_offset; } + + Node* end_container() { return m_end_container; } + unsigned end_offset() { return m_end_offset; } + + bool collapsed() + { + return start_container() == end_container() && start_offset() == end_offset(); + } + + void set_start(Node& container, unsigned offset) + { + m_start_container = container; + m_start_offset = offset; + } + + void set_end(Node& container, unsigned offset) + { + m_end_container = container; + m_end_offset = offset; + } + + NonnullRefPtr<Range> inverted() const; + NonnullRefPtr<Range> normalized() const; + NonnullRefPtr<Range> clone_range() const; + +private: + explicit Range(Window&); + Range(Node& start_container, size_t start_offset, Node& end_container, size_t end_offset); + + NonnullRefPtr<Node> m_start_container; + unsigned m_start_offset; + + NonnullRefPtr<Node> m_end_container; + unsigned m_end_offset; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/ShadowRoot.cpp b/Userland/Libraries/LibWeb/DOM/ShadowRoot.cpp new file mode 100644 index 0000000000..237216379b --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/ShadowRoot.cpp @@ -0,0 +1,49 @@ +/* + * 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 <LibWeb/DOM/Event.h> +#include <LibWeb/DOM/ShadowRoot.h> + +namespace Web::DOM { + +ShadowRoot::ShadowRoot(Document& document, Element& host) + : DocumentFragment(document) +{ + set_host(host); +} + +EventTarget* ShadowRoot::get_parent(const Event& event) +{ + if (!event.composed()) { + auto& events_first_invocation_target = downcast<Node>(*event.path().first().invocation_target); + if (events_first_invocation_target.root() == this) + return nullptr; + } + + return host(); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/ShadowRoot.h b/Userland/Libraries/LibWeb/DOM/ShadowRoot.h new file mode 100644 index 0000000000..4bee76470d --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/ShadowRoot.h @@ -0,0 +1,58 @@ +/* + * 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 <LibWeb/DOM/DocumentFragment.h> + +namespace Web::DOM { + +class ShadowRoot final : public DocumentFragment { +public: + ShadowRoot(Document&, Element&); + + bool closed() const { return m_closed; } + + bool delegates_focus() const { return m_delegates_focus; } + void set_delegates_focus(bool delegates_focus) { m_delegates_focus = delegates_focus; } + + bool available_to_element_internals() const { return m_available_to_element_internals; } + void set_available_to_element_internals(bool available_to_element_internals) { m_available_to_element_internals = available_to_element_internals; } + + // ^EventTarget + virtual EventTarget* get_parent(const Event&) override; + + // NOTE: This is intended for the JS bindings. + String mode() const { return m_closed ? "closed" : "open"; } + +private: + // NOTE: The specification doesn't seem to specify a default value for closed. Assuming false for now. + bool m_closed { false }; + bool m_delegates_focus { false }; + bool m_available_to_element_internals { false }; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/ShadowRoot.idl b/Userland/Libraries/LibWeb/DOM/ShadowRoot.idl new file mode 100644 index 0000000000..320ca53e80 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/ShadowRoot.idl @@ -0,0 +1,6 @@ +interface ShadowRoot : DocumentFragment { + + readonly attribute DOMString mode; + readonly attribute Element host; + +}; diff --git a/Userland/Libraries/LibWeb/DOM/Text.cpp b/Userland/Libraries/LibWeb/DOM/Text.cpp new file mode 100644 index 0000000000..de7beec7b6 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Text.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-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 <LibWeb/DOM/Text.h> +#include <LibWeb/Layout/TextNode.h> + +namespace Web::DOM { + +Text::Text(Document& document, const String& data) + : CharacterData(document, NodeType::TEXT_NODE, data) +{ +} + +Text::~Text() +{ +} + +RefPtr<Layout::Node> Text::create_layout_node() +{ + return adopt(*new Layout::TextNode(document(), *this)); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/Text.h b/Userland/Libraries/LibWeb/DOM/Text.h new file mode 100644 index 0000000000..0e6f4a2dbd --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Text.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-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 <AK/FlyString.h> +#include <AK/String.h> +#include <LibWeb/DOM/CharacterData.h> + +namespace Web::DOM { + +class Text final : public CharacterData { +public: + using WrapperType = Bindings::TextWrapper; + + explicit Text(Document&, const String&); + virtual ~Text() override; + + virtual FlyString node_name() const override { return "#text"; } + +private: + virtual RefPtr<Layout::Node> create_layout_node() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/Text.idl b/Userland/Libraries/LibWeb/DOM/Text.idl new file mode 100644 index 0000000000..25a6fdb405 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Text.idl @@ -0,0 +1,3 @@ +interface Text : CharacterData { + +}; diff --git a/Userland/Libraries/LibWeb/DOM/Timer.cpp b/Userland/Libraries/LibWeb/DOM/Timer.cpp new file mode 100644 index 0000000000..04e3c778f5 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Timer.cpp @@ -0,0 +1,60 @@ +/* + * 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 <LibCore/Timer.h> +#include <LibJS/Runtime/Function.h> +#include <LibWeb/DOM/Timer.h> +#include <LibWeb/DOM/Window.h> + +namespace Web::DOM { + +NonnullRefPtr<Timer> Timer::create_interval(Window& window, int milliseconds, JS::Function& callback) +{ + return adopt(*new Timer(window, Type::Interval, milliseconds, callback)); +} + +NonnullRefPtr<Timer> Timer::create_timeout(Window& window, int milliseconds, JS::Function& callback) +{ + return adopt(*new Timer(window, Type::Timeout, milliseconds, callback)); +} + +Timer::Timer(Window& window, Type type, int milliseconds, JS::Function& callback) + : m_window(window) + , m_type(type) + , m_callback(JS::make_handle(&callback)) +{ + m_id = window.allocate_timer_id({}); + m_timer = Core::Timer::construct(milliseconds, [this] { m_window.timer_did_fire({}, *this); }); + if (m_type == Type::Timeout) + m_timer->set_single_shot(true); +} + +Timer::~Timer() +{ + m_window.deallocate_timer_id({}, m_id); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/Timer.h b/Userland/Libraries/LibWeb/DOM/Timer.h new file mode 100644 index 0000000000..bdecde92d8 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Timer.h @@ -0,0 +1,63 @@ +/* + * 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 <AK/Forward.h> +#include <LibCore/Forward.h> +#include <LibJS/Heap/Handle.h> +#include <LibWeb/Forward.h> + +namespace Web::DOM { + +class Timer final : public RefCounted<Timer> { +public: + enum class Type { + Interval, + Timeout, + }; + + static NonnullRefPtr<Timer> create_interval(Window&, int milliseconds, JS::Function&); + static NonnullRefPtr<Timer> create_timeout(Window&, int milliseconds, JS::Function&); + + ~Timer(); + + i32 id() const { return m_id; } + Type type() const { return m_type; } + + JS::Function& callback() { return *m_callback.cell(); } + +private: + Timer(Window&, Type, int ms, JS::Function&); + + Window& m_window; + RefPtr<Core::Timer> m_timer; + Type m_type; + int m_id { 0 }; + JS::Handle<JS::Function> m_callback; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/Window.cpp b/Userland/Libraries/LibWeb/DOM/Window.cpp new file mode 100644 index 0000000000..d10620bc1e --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Window.cpp @@ -0,0 +1,177 @@ +/* + * 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 <LibGUI/DisplayLink.h> +#include <LibGUI/MessageBox.h> +#include <LibJS/Runtime/Function.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/DOM/EventDispatcher.h> +#include <LibWeb/DOM/Timer.h> +#include <LibWeb/DOM/Window.h> +#include <LibWeb/HighResolutionTime/Performance.h> +#include <LibWeb/InProcessWebView.h> +#include <LibWeb/Page/Frame.h> + +namespace Web::DOM { + +NonnullRefPtr<Window> Window::create_with_document(Document& document) +{ + return adopt(*new Window(document)); +} + +Window::Window(Document& document) + : EventTarget(static_cast<Bindings::ScriptExecutionContext&>(document)) + , m_document(document) + , m_performance(make<HighResolutionTime::Performance>(*this)) +{ +} + +Window::~Window() +{ +} + +void Window::set_wrapper(Badge<Bindings::WindowObject>, Bindings::WindowObject& wrapper) +{ + m_wrapper = wrapper.make_weak_ptr(); +} + +void Window::alert(const String& message) +{ + if (auto* page = m_document.page()) + page->client().page_did_request_alert(message); +} + +bool Window::confirm(const String& message) +{ + auto confirm_result = GUI::MessageBox::show(nullptr, message, "Confirm", GUI::MessageBox::Type::Warning, GUI::MessageBox::InputType::OKCancel); + return confirm_result == GUI::Dialog::ExecResult::ExecOK; +} + +i32 Window::set_interval(JS::Function& callback, i32 interval) +{ + auto timer = Timer::create_interval(*this, interval, callback); + m_timers.set(timer->id(), timer); + return timer->id(); +} + +i32 Window::set_timeout(JS::Function& callback, i32 interval) +{ + auto timer = Timer::create_timeout(*this, interval, callback); + m_timers.set(timer->id(), timer); + return timer->id(); +} + +void Window::timer_did_fire(Badge<Timer>, Timer& timer) +{ + // We should not be here if there's no JS wrapper for the Window object. + ASSERT(wrapper()); + auto& vm = wrapper()->vm(); + + // NOTE: This protector pointer keeps the timer alive until the end of this function no matter what. + NonnullRefPtr protector(timer); + + if (timer.type() == Timer::Type::Timeout) { + m_timers.remove(timer.id()); + } + + [[maybe_unused]] auto rc = vm.call(timer.callback(), wrapper()); + if (vm.exception()) + vm.clear_exception(); +} + +i32 Window::allocate_timer_id(Badge<Timer>) +{ + return m_timer_id_allocator.allocate(); +} + +void Window::deallocate_timer_id(Badge<Timer>, i32 id) +{ + m_timer_id_allocator.deallocate(id); +} + +void Window::clear_timeout(i32 timer_id) +{ + m_timers.remove(timer_id); +} + +void Window::clear_interval(i32 timer_id) +{ + m_timers.remove(timer_id); +} + +i32 Window::request_animation_frame(JS::Function& callback) +{ + // FIXME: This is extremely fake! + static double fake_timestamp = 0; + + i32 link_id = GUI::DisplayLink::register_callback([handle = make_handle(&callback)](i32 link_id) { + auto& function = const_cast<JS::Function&>(static_cast<const JS::Function&>(*handle.cell())); + auto& vm = function.vm(); + fake_timestamp += 10; + [[maybe_unused]] auto rc = vm.call(function, {}, JS::Value(fake_timestamp)); + if (vm.exception()) + vm.clear_exception(); + GUI::DisplayLink::unregister_callback(link_id); + }); + + // FIXME: Don't hand out raw DisplayLink ID's to JavaScript! + return link_id; +} + +void Window::cancel_animation_frame(i32 id) +{ + // FIXME: We should not be passing untrusted numbers to DisplayLink::unregister_callback()! + GUI::DisplayLink::unregister_callback(id); +} + +void Window::did_set_location_href(Badge<Bindings::LocationObject>, const URL& new_href) +{ + auto* frame = document().frame(); + if (!frame) + return; + frame->loader().load(new_href, FrameLoader::Type::Navigation); +} + +void Window::did_call_location_reload(Badge<Bindings::LocationObject>) +{ + auto* frame = document().frame(); + if (!frame) + return; + frame->loader().load(document().url(), FrameLoader::Type::Reload); +} + +bool Window::dispatch_event(NonnullRefPtr<Event> event) +{ + return EventDispatcher::dispatch(*this, event, true); +} + +Bindings::EventTargetWrapper* Window::create_wrapper(JS::GlobalObject&) +{ + ASSERT_NOT_REACHED(); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/Window.h b/Userland/Libraries/LibWeb/DOM/Window.h new file mode 100644 index 0000000000..4118bebbe2 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Window.h @@ -0,0 +1,98 @@ +/* + * 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 <AK/Badge.h> +#include <AK/IDAllocator.h> +#include <AK/RefCounted.h> +#include <AK/RefPtr.h> +#include <LibWeb/Bindings/WindowObject.h> +#include <LibWeb/Bindings/Wrappable.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/DOM/EventTarget.h> + +namespace Web::DOM { + +class Window final + : public RefCounted<Window> + , public EventTarget { +public: + static NonnullRefPtr<Window> create_with_document(Document&); + ~Window(); + + using RefCounted::ref; + using RefCounted::unref; + + virtual void ref_event_target() override { RefCounted::ref(); } + virtual void unref_event_target() override { RefCounted::unref(); } + virtual bool dispatch_event(NonnullRefPtr<Event>) override; + virtual Bindings::EventTargetWrapper* create_wrapper(JS::GlobalObject&) override; + + const Document& document() const { return m_document; } + Document& document() { return m_document; } + + void alert(const String&); + bool confirm(const String&); + i32 request_animation_frame(JS::Function&); + void cancel_animation_frame(i32); + + i32 set_timeout(JS::Function&, i32); + i32 set_interval(JS::Function&, i32); + void clear_timeout(i32); + void clear_interval(i32); + + void did_set_location_href(Badge<Bindings::LocationObject>, const URL& new_href); + void did_call_location_reload(Badge<Bindings::LocationObject>); + + Bindings::WindowObject* wrapper() { return m_wrapper; } + const Bindings::WindowObject* wrapper() const { return m_wrapper; } + + void set_wrapper(Badge<Bindings::WindowObject>, Bindings::WindowObject&); + + i32 allocate_timer_id(Badge<Timer>); + void deallocate_timer_id(Badge<Timer>, i32); + void timer_did_fire(Badge<Timer>, Timer&); + + HighResolutionTime::Performance& performance() { return *m_performance; } + + const Event* current_event() const { return m_current_event; } + void set_current_event(Event* event) { m_current_event = event; } + +private: + explicit Window(Document&); + + Document& m_document; + WeakPtr<Bindings::WindowObject> m_wrapper; + + IDAllocator m_timer_id_allocator; + HashMap<int, NonnullRefPtr<Timer>> m_timers; + + NonnullOwnPtr<HighResolutionTime::Performance> m_performance; + RefPtr<Event> m_current_event; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/XMLHttpRequest.cpp b/Userland/Libraries/LibWeb/DOM/XMLHttpRequest.cpp new file mode 100644 index 0000000000..f085ade4fd --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/XMLHttpRequest.cpp @@ -0,0 +1,121 @@ +/* + * 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 <LibJS/Runtime/Function.h> +#include <LibWeb/Bindings/EventWrapper.h> +#include <LibWeb/Bindings/EventWrapperFactory.h> +#include <LibWeb/Bindings/XMLHttpRequestWrapper.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/DOM/EventDispatcher.h> +#include <LibWeb/DOM/EventListener.h> +#include <LibWeb/DOM/Window.h> +#include <LibWeb/DOM/XMLHttpRequest.h> +#include <LibWeb/HTML/EventNames.h> +#include <LibWeb/Loader/ResourceLoader.h> +#include <LibWeb/Origin.h> + +namespace Web { + +XMLHttpRequest::XMLHttpRequest(DOM::Window& window) + : EventTarget(static_cast<Bindings::ScriptExecutionContext&>(window.document())) + , m_window(window) +{ +} + +XMLHttpRequest::~XMLHttpRequest() +{ +} + +void XMLHttpRequest::set_ready_state(ReadyState ready_state) +{ + // FIXME: call onreadystatechange once we have that + m_ready_state = ready_state; +} + +String XMLHttpRequest::response_text() const +{ + if (m_response.is_null()) + return {}; + return String::copy(m_response); +} + +void XMLHttpRequest::open(const String& method, const String& url) +{ + m_method = method; + m_url = url; + set_ready_state(ReadyState::Opened); +} + +void XMLHttpRequest::send() +{ + URL request_url = m_window->document().complete_url(m_url); + dbg() << "XHR send from " << m_window->document().url() << " to " << request_url; + + // TODO: Add support for preflight requests to support CORS requests + Origin request_url_origin = Origin(request_url.protocol(), request_url.host(), request_url.port()); + + if (!m_window->document().origin().is_same(request_url_origin)) { + dbg() << "XHR failed to load: Same-Origin Policy violation: " << m_window->document().url() << " may not load " << request_url; + auto weak_this = make_weak_ptr(); + if (!weak_this) + return; + const_cast<XMLHttpRequest&>(*weak_this).set_ready_state(ReadyState::Done); + const_cast<XMLHttpRequest&>(*weak_this).dispatch_event(DOM::Event::create(HTML::EventNames::error)); + return; + } + + // FIXME: in order to properly set ReadyState::HeadersReceived and ReadyState::Loading, + // we need to make ResourceLoader give us more detailed updates than just "done" and "error". + ResourceLoader::the().load( + m_window->document().complete_url(m_url), + [weak_this = make_weak_ptr()](auto data, auto&) { + if (!weak_this) + return; + const_cast<XMLHttpRequest&>(*weak_this).m_response = ByteBuffer::copy(data); + const_cast<XMLHttpRequest&>(*weak_this).set_ready_state(ReadyState::Done); + const_cast<XMLHttpRequest&>(*weak_this).dispatch_event(DOM::Event::create(HTML::EventNames::load)); + }, + [weak_this = make_weak_ptr()](auto& error) { + if (!weak_this) + return; + dbg() << "XHR failed to load: " << error; + const_cast<XMLHttpRequest&>(*weak_this).set_ready_state(ReadyState::Done); + const_cast<XMLHttpRequest&>(*weak_this).dispatch_event(DOM::Event::create(HTML::EventNames::error)); + }); +} + +bool XMLHttpRequest::dispatch_event(NonnullRefPtr<DOM::Event> event) +{ + return DOM::EventDispatcher::dispatch(*this, move(event)); +} + +Bindings::EventTargetWrapper* XMLHttpRequest::create_wrapper(JS::GlobalObject& global_object) +{ + return wrap(global_object, *this); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/XMLHttpRequest.h b/Userland/Libraries/LibWeb/DOM/XMLHttpRequest.h new file mode 100644 index 0000000000..33a6038863 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/XMLHttpRequest.h @@ -0,0 +1,85 @@ +/* + * 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 <AK/ByteBuffer.h> +#include <AK/RefCounted.h> +#include <AK/Weakable.h> +#include <LibWeb/Bindings/Wrappable.h> +#include <LibWeb/DOM/EventTarget.h> + +namespace Web { + +class XMLHttpRequest final + : public RefCounted<XMLHttpRequest> + , public Weakable<XMLHttpRequest> + , public DOM::EventTarget + , public Bindings::Wrappable { +public: + enum class ReadyState { + Unsent, + Opened, + HeadersReceived, + Loading, + Done, + }; + + using WrapperType = Bindings::XMLHttpRequestWrapper; + + static NonnullRefPtr<XMLHttpRequest> create(DOM::Window& window) { return adopt(*new XMLHttpRequest(window)); } + + virtual ~XMLHttpRequest() override; + + using RefCounted::ref; + using RefCounted::unref; + + ReadyState ready_state() const { return m_ready_state; }; + String response_text() const; + void open(const String& method, const String& url); + void send(); + +private: + virtual void ref_event_target() override { ref(); } + virtual void unref_event_target() override { unref(); } + virtual bool dispatch_event(NonnullRefPtr<DOM::Event>) override; + virtual Bindings::EventTargetWrapper* create_wrapper(JS::GlobalObject&) override; + + void set_ready_state(ReadyState); + + explicit XMLHttpRequest(DOM::Window&); + + NonnullRefPtr<DOM::Window> m_window; + + ReadyState m_ready_state { ReadyState::Unsent }; + + String m_method; + String m_url; + + ByteBuffer m_response; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOMTreeModel.cpp b/Userland/Libraries/LibWeb/DOMTreeModel.cpp new file mode 100644 index 0000000000..76c0e39769 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOMTreeModel.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2018-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 "DOMTreeModel.h" +#include <AK/StringBuilder.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/DOM/Text.h> +#include <ctype.h> +#include <stdio.h> + +namespace Web { + +DOMTreeModel::DOMTreeModel(DOM::Document& document) + : m_document(document) +{ + m_document_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-html.png")); + m_element_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object.png")); + m_text_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-unknown.png")); +} + +DOMTreeModel::~DOMTreeModel() +{ +} + +GUI::ModelIndex DOMTreeModel::index(int row, int column, const GUI::ModelIndex& parent) const +{ + if (!parent.is_valid()) { + return create_index(row, column, m_document.ptr()); + } + auto& parent_node = *static_cast<DOM::Node*>(parent.internal_data()); + return create_index(row, column, parent_node.child_at_index(row)); +} + +GUI::ModelIndex DOMTreeModel::parent_index(const GUI::ModelIndex& index) const +{ + if (!index.is_valid()) + return {}; + auto& node = *static_cast<DOM::Node*>(index.internal_data()); + if (!node.parent()) + return {}; + + // FIXME: Handle the template element (child elements are not stored in it, all of its children are in its document fragment "content") + + // No grandparent? Parent is the document! + if (!node.parent()->parent()) { + return create_index(0, 0, m_document.ptr()); + } + + // Walk the grandparent's children to find the index of node's parent in its parent. + // (This is needed to produce the row number of the GUI::ModelIndex corresponding to node's parent.) + int grandparent_child_index = 0; + for (auto* grandparent_child = node.parent()->parent()->first_child(); grandparent_child; grandparent_child = grandparent_child->next_sibling()) { + if (grandparent_child == node.parent()) + return create_index(grandparent_child_index, 0, node.parent()); + ++grandparent_child_index; + } + + ASSERT_NOT_REACHED(); + return {}; +} + +int DOMTreeModel::row_count(const GUI::ModelIndex& index) const +{ + if (!index.is_valid()) + return 1; + auto& node = *static_cast<DOM::Node*>(index.internal_data()); + return node.child_count(); +} + +int DOMTreeModel::column_count(const GUI::ModelIndex&) const +{ + return 1; +} + +static String with_whitespace_collapsed(const StringView& string) +{ + StringBuilder builder; + for (size_t i = 0; i < string.length(); ++i) { + if (isspace(string[i])) { + builder.append(' '); + while (i < string.length()) { + if (isspace(string[i])) { + ++i; + continue; + } + builder.append(string[i]); + break; + } + continue; + } + builder.append(string[i]); + } + return builder.to_string(); +} + +GUI::Variant DOMTreeModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const +{ + auto& node = *static_cast<DOM::Node*>(index.internal_data()); + if (role == GUI::ModelRole::Icon) { + if (node.is_document()) + return m_document_icon; + if (node.is_element()) + return m_element_icon; + // FIXME: More node type icons? + return m_text_icon; + } + if (role == GUI::ModelRole::Display) { + if (node.is_text()) + return with_whitespace_collapsed(downcast<DOM::Text>(node).data()); + if (!node.is_element()) + return node.node_name(); + auto& element = downcast<DOM::Element>(node); + StringBuilder builder; + builder.append('<'); + builder.append(element.local_name()); + element.for_each_attribute([&](auto& name, auto& value) { + builder.append(' '); + builder.append(name); + builder.append('='); + builder.append('"'); + builder.append(value); + builder.append('"'); + }); + builder.append('>'); + return builder.to_string(); + } + return {}; +} + +void DOMTreeModel::update() +{ + did_update(); +} + +} diff --git a/Userland/Libraries/LibWeb/DOMTreeModel.h b/Userland/Libraries/LibWeb/DOMTreeModel.h new file mode 100644 index 0000000000..42a568b306 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOMTreeModel.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-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 <LibGUI/Model.h> +#include <LibWeb/Forward.h> + +namespace Web { + +class DOMTreeModel final : public GUI::Model { +public: + static NonnullRefPtr<DOMTreeModel> create(DOM::Document& document) + { + return adopt(*new DOMTreeModel(document)); + } + + virtual ~DOMTreeModel() override; + + virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; + virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; + virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; + virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override; + virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override; + virtual void update() override; + +private: + explicit DOMTreeModel(DOM::Document&); + + NonnullRefPtr<DOM::Document> m_document; + + GUI::Icon m_document_icon; + GUI::Icon m_element_icon; + GUI::Icon m_text_icon; +}; + +} diff --git a/Userland/Libraries/LibWeb/Dump.cpp b/Userland/Libraries/LibWeb/Dump.cpp new file mode 100644 index 0000000000..f8f83ebc87 --- /dev/null +++ b/Userland/Libraries/LibWeb/Dump.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2018-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/QuickSort.h> +#include <AK/StringBuilder.h> +#include <AK/Utf8View.h> +#include <LibWeb/CSS/PropertyID.h> +#include <LibWeb/CSS/StyleSheet.h> +#include <LibWeb/DOM/Comment.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/DocumentFragment.h> +#include <LibWeb/DOM/DocumentType.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/DOM/Text.h> +#include <LibWeb/Dump.h> +#include <LibWeb/HTML/HTMLTemplateElement.h> +#include <LibWeb/Layout/BlockBox.h> +#include <LibWeb/Layout/Node.h> +#include <LibWeb/Layout/TextNode.h> +#include <stdio.h> + +namespace Web { + +void dump_tree(const DOM::Node& node) +{ + static int indent = 0; + for (int i = 0; i < indent; ++i) + dbgprintf(" "); + if (is<DOM::Document>(node)) { + dbgprintf("*Document*\n"); + } else if (is<DOM::Element>(node)) { + dbgprintf("<%s", downcast<DOM::Element>(node).local_name().characters()); + downcast<DOM::Element>(node).for_each_attribute([](auto& name, auto& value) { + dbgprintf(" %s=%s", name.characters(), value.characters()); + }); + dbgprintf(">\n"); + } else if (is<DOM::Text>(node)) { + dbgprintf("\"%s\"\n", downcast<DOM::Text>(node).data().characters()); + } else if (is<DOM::DocumentType>(node)) { + dbgprintf("<!DOCTYPE html>\n"); + } else if (is<DOM::Comment>(node)) { + dbgprintf("<!--%s-->\n", downcast<DOM::Comment>(node).data().characters()); + } else if (is<DOM::DocumentFragment>(node)) { + dbgprintf("#document-fragment\n"); + } + ++indent; + if (is<DOM::ParentNode>(node)) { + if (!is<HTML::HTMLTemplateElement>(node)) { + static_cast<const DOM::ParentNode&>(node).for_each_child([](auto& child) { + dump_tree(child); + }); + } else { + auto& template_element = downcast<HTML::HTMLTemplateElement>(node); + dump_tree(template_element.content()); + } + } + --indent; +} + +void dump_tree(const Layout::Node& layout_node, bool show_box_model, bool show_specified_style) +{ + StringBuilder builder; + dump_tree(builder, layout_node, show_box_model, show_specified_style, true); + dbgprintf("%s", builder.to_string().characters()); +} + +void dump_tree(StringBuilder& builder, const Layout::Node& layout_node, bool show_box_model, bool show_specified_style, bool interactive) +{ + static size_t indent = 0; + for (size_t i = 0; i < indent; ++i) + builder.append(" "); + + FlyString tag_name; + if (layout_node.is_anonymous()) + tag_name = "(anonymous)"; + else if (is<DOM::Text>(layout_node.dom_node())) + tag_name = "#text"; + else if (is<DOM::Document>(layout_node.dom_node())) + tag_name = "#document"; + else if (is<DOM::Element>(layout_node.dom_node())) + tag_name = downcast<DOM::Element>(*layout_node.dom_node()).local_name(); + else + tag_name = "???"; + + String identifier = ""; + if (layout_node.dom_node() && is<DOM::Element>(*layout_node.dom_node())) { + auto& element = downcast<DOM::Element>(*layout_node.dom_node()); + StringBuilder builder; + auto id = element.attribute(HTML::AttributeNames::id); + if (!id.is_empty()) { + builder.append('#'); + builder.append(id); + } + for (auto& class_name : element.class_names()) { + builder.append('.'); + builder.append(class_name); + } + identifier = builder.to_string(); + } + + const char* nonbox_color_on = ""; + const char* box_color_on = ""; + const char* positioned_color_on = ""; + const char* floating_color_on = ""; + const char* inline_block_color_on = ""; + const char* line_box_color_on = ""; + const char* fragment_color_on = ""; + const char* color_off = ""; + + if (interactive) { + nonbox_color_on = "\033[33m"; + box_color_on = "\033[34m"; + positioned_color_on = "\033[31;1m"; + floating_color_on = "\033[32;1m"; + inline_block_color_on = "\033[36;1m"; + line_box_color_on = "\033[34;1m"; + fragment_color_on = "\033[35;1m"; + color_off = "\033[0m"; + } + + if (!is<Layout::Box>(layout_node)) { + builder.appendff("{}{}{} <{}{}{}>", + nonbox_color_on, + layout_node.class_name().substring_view(13), + color_off, + tag_name, + nonbox_color_on, + identifier, + color_off); + if (interactive) + builder.appendff(" @{:p}", &layout_node); + builder.append("\n"); + } else { + auto& box = downcast<Layout::Box>(layout_node); + builder.appendff("{}{}{} <{}{}{}{}> ", + box_color_on, + box.class_name().substring_view(13), + color_off, + box_color_on, + tag_name, + color_off, + identifier.characters()); + + if (interactive) + builder.appendff("@{:p} ", &layout_node); + + builder.appendff("at ({},{}) size {}x{}", + (int)box.absolute_x(), + (int)box.absolute_y(), + (int)box.width(), + (int)box.height()); + + if (box.is_positioned()) + builder.appendff(" {}positioned{}", positioned_color_on, color_off); + if (box.is_floating()) + builder.appendff(" {}floating{}", floating_color_on, color_off); + if (box.is_inline_block()) + builder.appendff(" {}inline-block{}", inline_block_color_on, color_off); + + if (show_box_model) { + // Dump the horizontal box properties + builder.appendf(" [%g+%g+%g %g %g+%g+%g]", + box.box_model().margin.left, + box.box_model().border.left, + box.box_model().padding.left, + box.width(), + box.box_model().padding.right, + box.box_model().border.right, + box.box_model().margin.right); + + // And the vertical box properties + builder.appendf(" [%g+%g+%g %g %g+%g+%g]", + box.box_model().margin.top, + box.box_model().border.top, + box.box_model().padding.top, + box.height(), + box.box_model().padding.bottom, + box.box_model().border.bottom, + box.box_model().margin.bottom); + } + + builder.append("\n"); + } + + if (is<Layout::BlockBox>(layout_node) && static_cast<const Layout::BlockBox&>(layout_node).children_are_inline()) { + auto& block = static_cast<const Layout::BlockBox&>(layout_node); + for (size_t line_box_index = 0; line_box_index < block.line_boxes().size(); ++line_box_index) { + auto& line_box = block.line_boxes()[line_box_index]; + for (size_t i = 0; i < indent; ++i) + builder.append(" "); + builder.appendff(" {}line {}{} width: {}\n", + line_box_color_on, + line_box_index, + color_off, + (int)line_box.width()); + for (size_t fragment_index = 0; fragment_index < line_box.fragments().size(); ++fragment_index) { + auto& fragment = line_box.fragments()[fragment_index]; + for (size_t i = 0; i < indent; ++i) + builder.append(" "); + builder.appendff(" {}frag {}{} from {} ", + fragment_color_on, + fragment_index, + color_off, + fragment.layout_node().class_name()); + if (interactive) + builder.appendff("@{:p}, ", &fragment.layout_node()); + builder.appendff("start: {}, length: {}, rect: {}\n", + fragment.start(), + fragment.length(), + enclosing_int_rect(fragment.absolute_rect()).to_string()); + if (is<Layout::TextNode>(fragment.layout_node())) { + for (size_t i = 0; i < indent; ++i) + builder.append(" "); + auto& layout_text = static_cast<const Layout::TextNode&>(fragment.layout_node()); + auto fragment_text = layout_text.text_for_rendering().substring(fragment.start(), fragment.length()); + builder.appendff(" \"{}\"\n", fragment_text); + } + } + } + } + + if (show_specified_style && layout_node.dom_node() && layout_node.dom_node()->is_element() && downcast<DOM::Element>(layout_node.dom_node())->specified_css_values()) { + struct NameAndValue { + String name; + String value; + }; + Vector<NameAndValue> properties; + downcast<DOM::Element>(*layout_node.dom_node()).specified_css_values()->for_each_property([&](auto property_id, auto& value) { + properties.append({ CSS::string_from_property_id(property_id), value.to_string() }); + }); + quick_sort(properties, [](auto& a, auto& b) { return a.name < b.name; }); + + for (auto& property : properties) { + for (size_t i = 0; i < indent; ++i) + builder.append(" "); + builder.appendf(" (%s: %s)\n", property.name.characters(), property.value.characters()); + } + } + + ++indent; + layout_node.for_each_child([&](auto& child) { + dump_tree(builder, child, show_box_model, show_specified_style, interactive); + }); + --indent; +} + +void dump_selector(const CSS::Selector& selector) +{ + dbgprintf(" CSS::Selector:\n"); + + for (auto& complex_selector : selector.complex_selectors()) { + dbgprintf(" "); + + const char* relation_description = ""; + switch (complex_selector.relation) { + case CSS::Selector::ComplexSelector::Relation::None: + relation_description = "None"; + break; + case CSS::Selector::ComplexSelector::Relation::ImmediateChild: + relation_description = "ImmediateChild"; + break; + case CSS::Selector::ComplexSelector::Relation::Descendant: + relation_description = "Descendant"; + break; + case CSS::Selector::ComplexSelector::Relation::AdjacentSibling: + relation_description = "AdjacentSibling"; + break; + case CSS::Selector::ComplexSelector::Relation::GeneralSibling: + relation_description = "GeneralSibling"; + break; + } + + if (*relation_description) + dbgprintf("{%s} ", relation_description); + + for (size_t i = 0; i < complex_selector.compound_selector.size(); ++i) { + auto& simple_selector = complex_selector.compound_selector[i]; + const char* type_description = "Unknown"; + switch (simple_selector.type) { + case CSS::Selector::SimpleSelector::Type::Invalid: + type_description = "Invalid"; + break; + case CSS::Selector::SimpleSelector::Type::Universal: + type_description = "Universal"; + break; + case CSS::Selector::SimpleSelector::Type::Id: + type_description = "Id"; + break; + case CSS::Selector::SimpleSelector::Type::Class: + type_description = "Class"; + break; + case CSS::Selector::SimpleSelector::Type::TagName: + type_description = "TagName"; + break; + } + const char* attribute_match_type_description = ""; + switch (simple_selector.attribute_match_type) { + case CSS::Selector::SimpleSelector::AttributeMatchType::None: + break; + case CSS::Selector::SimpleSelector::AttributeMatchType::HasAttribute: + attribute_match_type_description = "HasAttribute"; + break; + case CSS::Selector::SimpleSelector::AttributeMatchType::ExactValueMatch: + attribute_match_type_description = "ExactValueMatch"; + break; + case CSS::Selector::SimpleSelector::AttributeMatchType::Contains: + attribute_match_type_description = "Contains"; + break; + } + + const char* pseudo_class_description = ""; + switch (simple_selector.pseudo_class) { + case CSS::Selector::SimpleSelector::PseudoClass::Link: + pseudo_class_description = "Link"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Visited: + pseudo_class_description = "Visited"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::None: + pseudo_class_description = "None"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Root: + pseudo_class_description = "Root"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Focus: + pseudo_class_description = "Focus"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Empty: + pseudo_class_description = "Empty"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Hover: + pseudo_class_description = "Hover"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::LastChild: + pseudo_class_description = "LastChild"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::FirstChild: + pseudo_class_description = "FirstChild"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::OnlyChild: + pseudo_class_description = "OnlyChild"; + break; + } + + dbgprintf("%s:%s", type_description, simple_selector.value.characters()); + if (simple_selector.pseudo_class != CSS::Selector::SimpleSelector::PseudoClass::None) + dbgprintf(" pseudo_class=%s", pseudo_class_description); + if (simple_selector.attribute_match_type != CSS::Selector::SimpleSelector::AttributeMatchType::None) { + dbgprintf(" [%s, name='%s', value='%s']", attribute_match_type_description, simple_selector.attribute_name.characters(), simple_selector.attribute_value.characters()); + } + + if (i != complex_selector.compound_selector.size() - 1) + dbgprintf(", "); + } + dbgprintf("\n"); + } +} + +void dump_rule(const CSS::StyleRule& rule) +{ + dbgprintf("Rule:\n"); + for (auto& selector : rule.selectors()) { + dump_selector(selector); + } + dbgprintf(" Declarations:\n"); + for (auto& property : rule.declaration().properties()) { + dbgprintf(" %s: '%s'\n", CSS::string_from_property_id(property.property_id), property.value->to_string().characters()); + } +} + +void dump_sheet(const CSS::StyleSheet& sheet) +{ + dbgprintf("StyleSheet{%p}: %zu rule(s)\n", &sheet, sheet.rules().size()); + + for (auto& rule : sheet.rules()) { + dump_rule(rule); + } +} + +} diff --git a/Userland/Libraries/LibWeb/Dump.h b/Userland/Libraries/LibWeb/Dump.h new file mode 100644 index 0000000000..ab1d271a42 --- /dev/null +++ b/Userland/Libraries/LibWeb/Dump.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-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 <AK/Forward.h> +#include <LibWeb/Forward.h> + +namespace Web { + +void dump_tree(const DOM::Node&); +void dump_tree(StringBuilder&, const Layout::Node&, bool show_box_model = false, bool show_specified_style = false, bool colorize = false); +void dump_tree(const Layout::Node&, bool show_box_model = false, bool show_specified_style = false); +void dump_sheet(const CSS::StyleSheet&); +void dump_rule(const CSS::StyleRule&); +void dump_selector(const CSS::Selector&); + +} diff --git a/Userland/Libraries/LibWeb/DumpLayoutTree/CMakeLists.txt b/Userland/Libraries/LibWeb/DumpLayoutTree/CMakeLists.txt new file mode 100644 index 0000000000..11a7ce62f5 --- /dev/null +++ b/Userland/Libraries/LibWeb/DumpLayoutTree/CMakeLists.txt @@ -0,0 +1,6 @@ +set(SOURCES + main.cpp +) + +serenity_bin(DumpLayoutTree) +target_link_libraries(DumpLayoutTree LibWeb) diff --git a/Userland/Libraries/LibWeb/DumpLayoutTree/main.cpp b/Userland/Libraries/LibWeb/DumpLayoutTree/main.cpp new file mode 100644 index 0000000000..f1def66b6e --- /dev/null +++ b/Userland/Libraries/LibWeb/DumpLayoutTree/main.cpp @@ -0,0 +1,60 @@ +/* + * 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 <LibCore/ArgsParser.h> +#include <LibGUI/Application.h> +#include <LibWeb/Dump.h> +#include <LibWeb/InProcessWebView.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> +#include <stdlib.h> + +int main(int argc, char** argv) +{ + auto app = GUI::Application::construct(argc, argv); + auto window = GUI::Window::construct(); + window->set_title("DumpLayoutTree"); + window->resize(800, 600); + window->show(); + auto& web_view = window->set_main_widget<Web::InProcessWebView>(); + web_view.load(URL::create_with_file_protocol(argv[1])); + web_view.on_load_finish = [&](auto&) { + auto* document = web_view.document(); + if (!document) { + warnln("No document."); + _exit(1); + } + auto* layout_root = document->layout_node(); + if (!layout_root) { + warnln("No layout tree."); + _exit(1); + } + StringBuilder builder; + Web::dump_tree(builder, *layout_root); + write(STDOUT_FILENO, builder.string_view().characters_without_null_termination(), builder.length()); + _exit(0); + }; + return app->exec(); +} diff --git a/Userland/Libraries/LibWeb/FontCache.cpp b/Userland/Libraries/LibWeb/FontCache.cpp new file mode 100644 index 0000000000..c2ba8e7050 --- /dev/null +++ b/Userland/Libraries/LibWeb/FontCache.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-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 <LibGfx/Font.h> +#include <LibWeb/FontCache.h> + +FontCache& FontCache::the() +{ + static FontCache cache; + return cache; +} + +RefPtr<Gfx::Font> FontCache::get(const FontSelector& font_selector) const +{ + auto cached_font = m_fonts.get(font_selector); + if (cached_font.has_value()) + return cached_font.value(); + return nullptr; +} + +void FontCache::set(const FontSelector& font_selector, NonnullRefPtr<Gfx::Font> font) +{ + m_fonts.set(font_selector, move(font)); +} diff --git a/Userland/Libraries/LibWeb/FontCache.h b/Userland/Libraries/LibWeb/FontCache.h new file mode 100644 index 0000000000..8cc554a8f4 --- /dev/null +++ b/Userland/Libraries/LibWeb/FontCache.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-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 <AK/FlyString.h> +#include <AK/HashMap.h> +#include <AK/String.h> +#include <LibGfx/Forward.h> + +struct FontSelector { + FlyString family; + int size { 0 }; + int weight { 0 }; + + bool operator==(const FontSelector& other) const + { + return family == other.family && size == other.size && weight == other.weight; + } +}; + +namespace AK { +template<> +struct Traits<FontSelector> : public GenericTraits<FontSelector> { + static unsigned hash(const FontSelector& key) { return pair_int_hash(pair_int_hash(key.family.hash(), key.weight), key.size); } +}; +} + +class FontCache { +public: + static FontCache& the(); + RefPtr<Gfx::Font> get(const FontSelector&) const; + void set(const FontSelector&, NonnullRefPtr<Gfx::Font>); + +private: + FontCache() { } + mutable HashMap<FontSelector, NonnullRefPtr<Gfx::Font>> m_fonts; +}; diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h new file mode 100644 index 0000000000..445b7222fa --- /dev/null +++ b/Userland/Libraries/LibWeb/Forward.h @@ -0,0 +1,297 @@ +/* + * 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 + +namespace Web::CSS { +class Length; +class Selector; +class StyleDeclaration; +class StyleProperties; +class StyleResolver; +class StyleRule; +class StyleSheet; +enum class Display; +} + +namespace Web::DOM { +class CharacterData; +class Comment; +class Document; +class DocumentFragment; +class DocumentType; +class DOMImplementation; +class Element; +class Event; +class EventHandler; +class EventListener; +class EventTarget; +class MouseEvent; +class Node; +class ParentNode; +class Position; +class Text; +class Timer; +class Window; +class Range; +enum class QuirksMode; +} + +namespace Web::HTML { +class CanvasRenderingContext2D; +class HTMLAnchorElement; +class HTMLAreaElement; +class HTMLAudioElement; +class HTMLBaseElement; +class HTMLBlinkElement; +class HTMLBodyElement; +class HTMLBRElement; +class HTMLButtonElement; +class HTMLCanvasElement; +class HTMLDataElement; +class HTMLDataListElement; +class HTMLDetailsElement; +class HTMLDialogElement; +class HTMLDirectoryElement; +class HTMLDivElement; +class HTMLDListElement; +class HTMLDocumentParser; +class HTMLElement; +class HTMLEmbedElement; +class HTMLFieldSetElement; +class HTMLFontElement; +class HTMLFormElement; +class HTMLFrameElement; +class HTMLFrameSetElement; +class HTMLHeadElement; +class HTMLHeadingElement; +class HTMLHRElement; +class HTMLHtmlElement; +class HTMLIFrameElement; +class HTMLImageElement; +class HTMLInputElement; +class HTMLLabelElement; +class HTMLLegendElement; +class HTMLLIElement; +class HTMLLinkElement; +class HTMLMapElement; +class HTMLMarqueeElement; +class HTMLMediaElement; +class HTMLMenuElement; +class HTMLMetaElement; +class HTMLMeterElement; +class HTMLModElement; +class HTMLObjectElement; +class HTMLOListElement; +class HTMLOptGroupElement; +class HTMLOptionElement; +class HTMLOutputElement; +class HTMLParagraphElement; +class HTMLParamElement; +class HTMLPictureElement; +class HTMLPreElement; +class HTMLProgressElement; +class HTMLQuoteElement; +class HTMLScriptElement; +class HTMLSelectElement; +class HTMLSlotElement; +class HTMLSourceElement; +class HTMLSpanElement; +class HTMLStyleElement; +class HTMLTableCaptionElement; +class HTMLTableCellElement; +class HTMLTableColElement; +class HTMLTableElement; +class HTMLTableRowElement; +class HTMLTableSectionElement; +class HTMLTemplateElement; +class HTMLTextAreaElement; +class HTMLTimeElement; +class HTMLTitleElement; +class HTMLTrackElement; +class HTMLUListElement; +class HTMLUnknownElement; +class HTMLVideoElement; +class ImageData; +} + +namespace Web::HighResolutionTime { +class Performance; +} + +namespace Web::SVG { +class SVGElement; +class SVGGeometryElement; +class SVGGraphicsElement; +class SVGPathElement; +class SVGSVGElement; +} + +namespace Web::Layout { +enum class LayoutMode; +enum class PaintPhase; +class BlockBox; +class BlockFormattingContext; +class Box; +class ButtonBox; +class CheckBox; +class FormattingContext; +class InitialContainingBlockBox; +class InlineFormattingContext; +class LineBox; +class LineBoxFragment; +class Node; +class NodeWithStyle; +class ReplacedBox; +} + +namespace Web { +class EventHandler; +class EditEventHandler; +class Frame; +class FrameLoader; +class InProcessWebView; +class LoadRequest; +class Origin; +class OutOfProcessWebView; +class Page; +class PageClient; +class PaintContext; +class Resource; +class ResourceLoader; +class StackingContext; +class XMLHttpRequest; +} + +namespace Web::Bindings { + +class CanvasRenderingContext2DWrapper; +class CharacterDataWrapper; +class CommentWrapper; +class DocumentFragmentWrapper; +class DocumentTypeWrapper; +class DocumentWrapper; +class DOMImplementationWrapper; +class ElementWrapper; +class EventListenerWrapper; +class EventTargetWrapper; +class EventWrapper; +class HTMLAnchorElementWrapper; +class HTMLAreaElementWrapper; +class HTMLAudioElementWrapper; +class HTMLBaseElementWrapper; +class HTMLBodyElementWrapper; +class HTMLBRElementWrapper; +class HTMLButtonElementWrapper; +class HTMLCanvasElementWrapper; +class HTMLDataElementWrapper; +class HTMLDataListElementWrapper; +class HTMLDetailsElementWrapper; +class HTMLDialogElementWrapper; +class HTMLDirectoryElementWrapper; +class HTMLDivElementWrapper; +class HTMLDListElementWrapper; +class HTMLElementWrapper; +class HTMLEmbedElementWrapper; +class HTMLFieldSetElementWrapper; +class HTMLFontElementWrapper; +class HTMLFormElementWrapper; +class HTMLFrameElementWrapper; +class HTMLFrameSetElementWrapper; +class HTMLHRElementWrapper; +class HTMLHeadElementWrapper; +class HTMLHeadingElementWrapper; +class HTMLHtmlElementWrapper; +class HTMLIFrameElementWrapper; +class HTMLImageElementWrapper; +class HTMLInputElementWrapper; +class HTMLLabelElementWrapper; +class HTMLLegendElementWrapper; +class HTMLLIElementWrapper; +class HTMLLinkElementWrapper; +class HTMLMapElementWrapper; +class HTMLMarqueeElementWrapper; +class HTMLMediaElementWrapper; +class HTMLMenuElementWrapper; +class HTMLMetaElementWrapper; +class HTMLMeterElementWrapper; +class HTMLModElementWrapper; +class HTMLObjectElementWrapper; +class HTMLOListElementWrapper; +class HTMLOptGroupElementWrapper; +class HTMLOptionElementWrapper; +class HTMLOutputElementWrapper; +class HTMLParagraphElementWrapper; +class HTMLParamElementWrapper; +class HTMLPictureElementWrapper; +class HTMLPreElementWrapper; +class HTMLProgressElementWrapper; +class HTMLQuoteElementWrapper; +class HTMLScriptElementWrapper; +class HTMLSelectElementWrapper; +class HTMLSlotElementWrapper; +class HTMLSourceElementWrapper; +class HTMLSpanElementWrapper; +class HTMLStyleElementWrapper; +class HTMLTableCaptionElementWrapper; +class HTMLTableCellElementWrapper; +class HTMLTableColElementWrapper; +class HTMLTableElementWrapper; +class HTMLTableRowElementWrapper; +class HTMLTableSectionElementWrapper; +class HTMLTemplateElementWrapper; +class HTMLTextAreaElementWrapper; +class HTMLTimeElementWrapper; +class HTMLTitleElementWrapper; +class HTMLTrackElementWrapper; +class HTMLUListElementWrapper; +class HTMLUnknownElementWrapper; +class HTMLVideoElementWrapper; +class ImageDataWrapper; +class LocationObject; +class MouseEventWrapper; +class NodeWrapper; +class PerformanceWrapper; +class ScriptExecutionContext; +class SubmitEventWrapper; +class SVGElementWrapper; +class SVGGeometryElementWrapper; +class SVGGraphicsElementWrapper; +class SVGPathElementWrapper; +class SVGSVGElementWrapper; +class TextWrapper; +class UIEventWrapper; +class WindowObject; +class Wrappable; +class Wrapper; +class XMLHttpRequestConstructor; +class XMLHttpRequestPrototype; +class XMLHttpRequestWrapper; +class RangeConstructor; +class RangePrototype; +class RangeWrapper; + +} diff --git a/Userland/Libraries/LibWeb/HTML/AttributeNames.cpp b/Userland/Libraries/LibWeb/HTML/AttributeNames.cpp new file mode 100644 index 0000000000..e7a21e6b39 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/AttributeNames.cpp @@ -0,0 +1,63 @@ +/* + * 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 <LibWeb/HTML/AttributeNames.h> + +namespace Web { +namespace HTML { +namespace AttributeNames { + +#define __ENUMERATE_HTML_ATTRIBUTE(name) FlyString name; +ENUMERATE_HTML_ATTRIBUTES +#undef __ENUMERATE_HTML_ATTRIBUTE + +[[gnu::constructor]] static void initialize() +{ + static bool s_initialized = false; + if (s_initialized) + return; + +#define __ENUMERATE_HTML_ATTRIBUTE(name) \ + name = #name; + ENUMERATE_HTML_ATTRIBUTES +#undef __ENUMERATE_HTML_ATTRIBUTE + + // NOTE: Special cases for C++ keywords. + class_ = "class"; + for_ = "for"; + default_ = "default"; + char_ = "char"; + + // NOTE: Special cases for attributes with dashes in them. + accept_charset = "accept-charset"; + http_equiv = "http-equiv"; + + s_initialized = true; +} + +} +} +} diff --git a/Userland/Libraries/LibWeb/HTML/AttributeNames.h b/Userland/Libraries/LibWeb/HTML/AttributeNames.h new file mode 100644 index 0000000000..670ec9bdb4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/AttributeNames.h @@ -0,0 +1,163 @@ +/* + * 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 <AK/FlyString.h> + +namespace Web { +namespace HTML { +namespace AttributeNames { + +#define ENUMERATE_HTML_ATTRIBUTES \ + __ENUMERATE_HTML_ATTRIBUTE(abbr) \ + __ENUMERATE_HTML_ATTRIBUTE(accept) \ + __ENUMERATE_HTML_ATTRIBUTE(accept_charset) \ + __ENUMERATE_HTML_ATTRIBUTE(action) \ + __ENUMERATE_HTML_ATTRIBUTE(align) \ + __ENUMERATE_HTML_ATTRIBUTE(alink) \ + __ENUMERATE_HTML_ATTRIBUTE(allow) \ + __ENUMERATE_HTML_ATTRIBUTE(allowfullscreen) \ + __ENUMERATE_HTML_ATTRIBUTE(alt) \ + __ENUMERATE_HTML_ATTRIBUTE(archive) \ + __ENUMERATE_HTML_ATTRIBUTE(async) \ + __ENUMERATE_HTML_ATTRIBUTE(autoplay) \ + __ENUMERATE_HTML_ATTRIBUTE(axis) \ + __ENUMERATE_HTML_ATTRIBUTE(background) \ + __ENUMERATE_HTML_ATTRIBUTE(behaviour) \ + __ENUMERATE_HTML_ATTRIBUTE(bgcolor) \ + __ENUMERATE_HTML_ATTRIBUTE(border) \ + __ENUMERATE_HTML_ATTRIBUTE(cellpadding) \ + __ENUMERATE_HTML_ATTRIBUTE(cellspacing) \ + __ENUMERATE_HTML_ATTRIBUTE(char_) \ + __ENUMERATE_HTML_ATTRIBUTE(charoff) \ + __ENUMERATE_HTML_ATTRIBUTE(charset) \ + __ENUMERATE_HTML_ATTRIBUTE(checked) \ + __ENUMERATE_HTML_ATTRIBUTE(cite) \ + __ENUMERATE_HTML_ATTRIBUTE(class_) \ + __ENUMERATE_HTML_ATTRIBUTE(clear) \ + __ENUMERATE_HTML_ATTRIBUTE(code) \ + __ENUMERATE_HTML_ATTRIBUTE(codetype) \ + __ENUMERATE_HTML_ATTRIBUTE(color) \ + __ENUMERATE_HTML_ATTRIBUTE(cols) \ + __ENUMERATE_HTML_ATTRIBUTE(colspan) \ + __ENUMERATE_HTML_ATTRIBUTE(compact) \ + __ENUMERATE_HTML_ATTRIBUTE(content) \ + __ENUMERATE_HTML_ATTRIBUTE(contenteditable) \ + __ENUMERATE_HTML_ATTRIBUTE(controls) \ + __ENUMERATE_HTML_ATTRIBUTE(coords) \ + __ENUMERATE_HTML_ATTRIBUTE(data) \ + __ENUMERATE_HTML_ATTRIBUTE(datetime) \ + __ENUMERATE_HTML_ATTRIBUTE(declare) \ + __ENUMERATE_HTML_ATTRIBUTE(default_) \ + __ENUMERATE_HTML_ATTRIBUTE(defer) \ + __ENUMERATE_HTML_ATTRIBUTE(disabled) \ + __ENUMERATE_HTML_ATTRIBUTE(download) \ + __ENUMERATE_HTML_ATTRIBUTE(direction) \ + __ENUMERATE_HTML_ATTRIBUTE(dirname) \ + __ENUMERATE_HTML_ATTRIBUTE(event) \ + __ENUMERATE_HTML_ATTRIBUTE(face) \ + __ENUMERATE_HTML_ATTRIBUTE(for_) \ + __ENUMERATE_HTML_ATTRIBUTE(formnovalidate) \ + __ENUMERATE_HTML_ATTRIBUTE(formtarget) \ + __ENUMERATE_HTML_ATTRIBUTE(frame) \ + __ENUMERATE_HTML_ATTRIBUTE(frameborder) \ + __ENUMERATE_HTML_ATTRIBUTE(headers) \ + __ENUMERATE_HTML_ATTRIBUTE(height) \ + __ENUMERATE_HTML_ATTRIBUTE(hidden) \ + __ENUMERATE_HTML_ATTRIBUTE(href) \ + __ENUMERATE_HTML_ATTRIBUTE(hreflang) \ + __ENUMERATE_HTML_ATTRIBUTE(http_equiv) \ + __ENUMERATE_HTML_ATTRIBUTE(id) \ + __ENUMERATE_HTML_ATTRIBUTE(imagesizes) \ + __ENUMERATE_HTML_ATTRIBUTE(imagesrcset) \ + __ENUMERATE_HTML_ATTRIBUTE(integrity) \ + __ENUMERATE_HTML_ATTRIBUTE(ismap) \ + __ENUMERATE_HTML_ATTRIBUTE(label) \ + __ENUMERATE_HTML_ATTRIBUTE(lang) \ + __ENUMERATE_HTML_ATTRIBUTE(link) \ + __ENUMERATE_HTML_ATTRIBUTE(longdesc) \ + __ENUMERATE_HTML_ATTRIBUTE(loop) \ + __ENUMERATE_HTML_ATTRIBUTE(max) \ + __ENUMERATE_HTML_ATTRIBUTE(marginheight) \ + __ENUMERATE_HTML_ATTRIBUTE(marginwidth) \ + __ENUMERATE_HTML_ATTRIBUTE(media) \ + __ENUMERATE_HTML_ATTRIBUTE(method) \ + __ENUMERATE_HTML_ATTRIBUTE(min) \ + __ENUMERATE_HTML_ATTRIBUTE(multiple) \ + __ENUMERATE_HTML_ATTRIBUTE(name) \ + __ENUMERATE_HTML_ATTRIBUTE(nohref) \ + __ENUMERATE_HTML_ATTRIBUTE(nomodule) \ + __ENUMERATE_HTML_ATTRIBUTE(noshade) \ + __ENUMERATE_HTML_ATTRIBUTE(novalidate) \ + __ENUMERATE_HTML_ATTRIBUTE(nowrap) \ + __ENUMERATE_HTML_ATTRIBUTE(open) \ + __ENUMERATE_HTML_ATTRIBUTE(pattern) \ + __ENUMERATE_HTML_ATTRIBUTE(ping) \ + __ENUMERATE_HTML_ATTRIBUTE(placeholder) \ + __ENUMERATE_HTML_ATTRIBUTE(playsinline) \ + __ENUMERATE_HTML_ATTRIBUTE(poster) \ + __ENUMERATE_HTML_ATTRIBUTE(readonly) \ + __ENUMERATE_HTML_ATTRIBUTE(rel) \ + __ENUMERATE_HTML_ATTRIBUTE(required) \ + __ENUMERATE_HTML_ATTRIBUTE(rev) \ + __ENUMERATE_HTML_ATTRIBUTE(reversed) \ + __ENUMERATE_HTML_ATTRIBUTE(rows) \ + __ENUMERATE_HTML_ATTRIBUTE(rules) \ + __ENUMERATE_HTML_ATTRIBUTE(scheme) \ + __ENUMERATE_HTML_ATTRIBUTE(scrolling) \ + __ENUMERATE_HTML_ATTRIBUTE(selected) \ + __ENUMERATE_HTML_ATTRIBUTE(shape) \ + __ENUMERATE_HTML_ATTRIBUTE(size) \ + __ENUMERATE_HTML_ATTRIBUTE(sizes) \ + __ENUMERATE_HTML_ATTRIBUTE(src) \ + __ENUMERATE_HTML_ATTRIBUTE(srcdoc) \ + __ENUMERATE_HTML_ATTRIBUTE(srclang) \ + __ENUMERATE_HTML_ATTRIBUTE(srcset) \ + __ENUMERATE_HTML_ATTRIBUTE(standby) \ + __ENUMERATE_HTML_ATTRIBUTE(step) \ + __ENUMERATE_HTML_ATTRIBUTE(style) \ + __ENUMERATE_HTML_ATTRIBUTE(summary) \ + __ENUMERATE_HTML_ATTRIBUTE(target) \ + __ENUMERATE_HTML_ATTRIBUTE(text) \ + __ENUMERATE_HTML_ATTRIBUTE(title) \ + __ENUMERATE_HTML_ATTRIBUTE(type) \ + __ENUMERATE_HTML_ATTRIBUTE(usemap) \ + __ENUMERATE_HTML_ATTRIBUTE(value) \ + __ENUMERATE_HTML_ATTRIBUTE(valuetype) \ + __ENUMERATE_HTML_ATTRIBUTE(valign) \ + __ENUMERATE_HTML_ATTRIBUTE(version) \ + __ENUMERATE_HTML_ATTRIBUTE(vlink) \ + __ENUMERATE_HTML_ATTRIBUTE(width) \ + __ENUMERATE_HTML_ATTRIBUTE(wrap) + +#define __ENUMERATE_HTML_ATTRIBUTE(name) extern FlyString name; +ENUMERATE_HTML_ATTRIBUTES +#undef __ENUMERATE_HTML_ATTRIBUTE + +} +} +} diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp new file mode 100644 index 0000000000..d029e77eb8 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -0,0 +1,227 @@ +/* + * 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/OwnPtr.h> +#include <LibGfx/Painter.h> +#include <LibWeb/Bindings/CanvasRenderingContext2DWrapper.h> +#include <LibWeb/HTML/CanvasRenderingContext2D.h> +#include <LibWeb/HTML/HTMLCanvasElement.h> +#include <LibWeb/HTML/HTMLImageElement.h> +#include <LibWeb/HTML/ImageData.h> + +namespace Web::HTML { + +CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement& element) + : m_element(element) +{ +} + +CanvasRenderingContext2D::~CanvasRenderingContext2D() +{ +} + +void CanvasRenderingContext2D::set_fill_style(String style) +{ + m_fill_style = Gfx::Color::from_string(style).value_or(Color::Black); +} + +String CanvasRenderingContext2D::fill_style() const +{ + return m_fill_style.to_string(); +} + +void CanvasRenderingContext2D::fill_rect(float x, float y, float width, float height) +{ + auto painter = this->painter(); + if (!painter) + return; + + auto rect = m_transform.map(Gfx::FloatRect(x, y, width, height)); + painter->fill_rect(enclosing_int_rect(rect), m_fill_style); + did_draw(rect); +} + +void CanvasRenderingContext2D::set_stroke_style(String style) +{ + m_stroke_style = Gfx::Color::from_string(style).value_or(Color::Black); +} + +String CanvasRenderingContext2D::stroke_style() const +{ + return m_fill_style.to_string(); +} + +void CanvasRenderingContext2D::stroke_rect(float x, float y, float width, float height) +{ + auto painter = this->painter(); + if (!painter) + return; + + auto rect = m_transform.map(Gfx::FloatRect(x, y, width, height)); + + auto top_left = m_transform.map(Gfx::FloatPoint(x, y)).to_type<int>(); + auto top_right = m_transform.map(Gfx::FloatPoint(x + width - 1, y)).to_type<int>(); + auto bottom_left = m_transform.map(Gfx::FloatPoint(x, y + height - 1)).to_type<int>(); + auto bottom_right = m_transform.map(Gfx::FloatPoint(x + width - 1, y + height - 1)).to_type<int>(); + + painter->draw_line(top_left, top_right, m_stroke_style, m_line_width); + painter->draw_line(top_right, bottom_right, m_stroke_style, m_line_width); + painter->draw_line(bottom_right, bottom_left, m_stroke_style, m_line_width); + painter->draw_line(bottom_left, top_left, m_stroke_style, m_line_width); + + did_draw(rect); +} + +void CanvasRenderingContext2D::draw_image(const HTMLImageElement& image_element, float x, float y) +{ + if (!image_element.bitmap()) + return; + + auto painter = this->painter(); + if (!painter) + return; + + auto src_rect = image_element.bitmap()->rect(); + Gfx::FloatRect dst_rect = { x, y, (float)image_element.bitmap()->width(), (float)image_element.bitmap()->height() }; + auto rect = m_transform.map(dst_rect); + + painter->draw_scaled_bitmap(enclosing_int_rect(rect), *image_element.bitmap(), src_rect); +} + +void CanvasRenderingContext2D::scale(float sx, float sy) +{ + dbg() << "CanvasRenderingContext2D::scale(): " << sx << ", " << sy; + m_transform.scale(sx, sy); +} + +void CanvasRenderingContext2D::translate(float tx, float ty) +{ + dbg() << "CanvasRenderingContext2D::translate(): " << tx << ", " << ty; + m_transform.translate(tx, ty); +} + +void CanvasRenderingContext2D::rotate(float radians) +{ + dbg() << "CanvasRenderingContext2D::rotate(): " << radians; + m_transform.rotate_radians(radians); +} + +void CanvasRenderingContext2D::did_draw(const Gfx::FloatRect&) +{ + // FIXME: Make use of the rect to reduce the invalidated area when possible. + if (!m_element) + return; + if (!m_element->layout_node()) + return; + m_element->layout_node()->set_needs_display(); +} + +OwnPtr<Gfx::Painter> CanvasRenderingContext2D::painter() +{ + if (!m_element) + return {}; + + if (!m_element->bitmap()) { + if (!m_element->create_bitmap()) + return {}; + } + + return make<Gfx::Painter>(*m_element->bitmap()); +} + +void CanvasRenderingContext2D::begin_path() +{ + m_path = Gfx::Path(); +} + +void CanvasRenderingContext2D::close_path() +{ + m_path.close(); +} + +void CanvasRenderingContext2D::move_to(float x, float y) +{ + m_path.move_to({ x, y }); +} + +void CanvasRenderingContext2D::line_to(float x, float y) +{ + m_path.line_to({ x, y }); +} + +void CanvasRenderingContext2D::quadratic_curve_to(float cx, float cy, float x, float y) +{ + m_path.quadratic_bezier_curve_to({ cx, cy }, { x, y }); +} + +void CanvasRenderingContext2D::stroke() +{ + auto painter = this->painter(); + if (!painter) + return; + + painter->stroke_path(m_path, m_stroke_style, m_line_width); +} + +void CanvasRenderingContext2D::fill(Gfx::Painter::WindingRule winding) +{ + auto painter = this->painter(); + if (!painter) + return; + + auto path = m_path; + path.close_all_subpaths(); + painter->fill_path(path, m_fill_style, winding); +} + +void CanvasRenderingContext2D::fill(const String& fill_rule) +{ + if (fill_rule == "evenodd") + return fill(Gfx::Painter::WindingRule::EvenOdd); + return fill(Gfx::Painter::WindingRule::Nonzero); +} + +RefPtr<ImageData> CanvasRenderingContext2D::create_image_data(int width, int height) const +{ + if (!wrapper()) { + dbgln("Hmm! Attempted to create ImageData for wrapper-less CRC2D."); + return {}; + } + return ImageData::create_with_size(wrapper()->global_object(), width, height); +} + +void CanvasRenderingContext2D::put_image_data(const ImageData& image_data, float x, float y) +{ + auto painter = this->painter(); + if (!painter) + return; + + painter->blit(Gfx::IntPoint(x, y), image_data.bitmap(), image_data.bitmap().rect()); + + did_draw(Gfx::FloatRect(x, y, image_data.width(), image_data.height())); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h new file mode 100644 index 0000000000..58cccfc6e6 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h @@ -0,0 +1,103 @@ +/* + * 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 <AK/RefCounted.h> +#include <LibGfx/AffineTransform.h> +#include <LibGfx/Color.h> +#include <LibGfx/Forward.h> +#include <LibGfx/Painter.h> +#include <LibGfx/Path.h> +#include <LibWeb/Bindings/Wrappable.h> + +namespace Web::HTML { + +class CanvasRenderingContext2D + : public RefCounted<CanvasRenderingContext2D> + , public Bindings::Wrappable { + + AK_MAKE_NONCOPYABLE(CanvasRenderingContext2D); + AK_MAKE_NONMOVABLE(CanvasRenderingContext2D); + +public: + using WrapperType = Bindings::CanvasRenderingContext2DWrapper; + + static NonnullRefPtr<CanvasRenderingContext2D> create(HTMLCanvasElement& element) { return adopt(*new CanvasRenderingContext2D(element)); } + ~CanvasRenderingContext2D(); + + void set_fill_style(String); + String fill_style() const; + + void set_stroke_style(String); + String stroke_style() const; + + void fill_rect(float x, float y, float width, float height); + void stroke_rect(float x, float y, float width, float height); + + void draw_image(const HTMLImageElement&, float x, float y); + + void scale(float sx, float sy); + void translate(float x, float y); + void rotate(float degrees); + + void set_line_width(float line_width) { m_line_width = line_width; } + float line_width() const { return m_line_width; } + + void begin_path(); + void close_path(); + void move_to(float x, float y); + void line_to(float x, float y); + void quadratic_curve_to(float cx, float cy, float x, float y); + void stroke(); + + // FIXME: We should only have one fill(), really. Fix the wrapper generator! + void fill(Gfx::Painter::WindingRule); + void fill(const String& fill_rule); + + RefPtr<ImageData> create_image_data(int width, int height) const; + void put_image_data(const ImageData&, float x, float y); + + HTMLCanvasElement* canvas() { return m_element; } + +private: + explicit CanvasRenderingContext2D(HTMLCanvasElement&); + + void did_draw(const Gfx::FloatRect&); + + OwnPtr<Gfx::Painter> painter(); + + WeakPtr<HTMLCanvasElement> m_element; + + Gfx::AffineTransform m_transform; + Gfx::Color m_fill_style; + Gfx::Color m_stroke_style; + float m_line_width { 1 }; + + Gfx::Path m_path; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.idl b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.idl new file mode 100644 index 0000000000..bcfa544c67 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.idl @@ -0,0 +1,29 @@ +interface CanvasRenderingContext2D { + + undefined fillRect(double x, double y, double w, double h); + undefined strokeRect(double x, double y, double w, double h); + + undefined scale(double x, double y); + undefined translate(double x, double y); + undefined rotate(double radians); + + undefined beginPath(); + undefined closePath(); + undefined fill(DOMString fillRule); + undefined stroke(); + undefined moveTo(double x, double y); + undefined lineTo(double x, double y); + undefined quadraticCurveTo(double cpx, double cpy, double x, double y); + + undefined drawImage(HTMLImageElement image, double dx, double dy); + + attribute DOMString fillStyle; + attribute DOMString strokeStyle; + attribute double lineWidth; + + ImageData createImageData(double sw, double sh); + undefined putImageData(ImageData imagedata, double dx, double dy); + + readonly attribute HTMLCanvasElement canvas; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/EventNames.cpp b/Userland/Libraries/LibWeb/HTML/EventNames.cpp new file mode 100644 index 0000000000..6856aa2d03 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/EventNames.cpp @@ -0,0 +1,49 @@ +/* + * 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 <LibWeb/HTML/EventNames.h> + +namespace Web::HTML::EventNames { + +#define __ENUMERATE_HTML_EVENT(name) FlyString name; +ENUMERATE_HTML_EVENTS +#undef __ENUMERATE_HTML_EVENT + +[[gnu::constructor]] static void initialize() +{ + static bool s_initialized = false; + if (s_initialized) + return; + +#define __ENUMERATE_HTML_EVENT(name) \ + name = #name; + ENUMERATE_HTML_EVENTS +#undef __ENUMERATE_HTML_EVENT + + s_initialized = true; +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/EventNames.h b/Userland/Libraries/LibWeb/HTML/EventNames.h new file mode 100644 index 0000000000..d36056ef01 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/EventNames.h @@ -0,0 +1,85 @@ +/* + * 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 <AK/FlyString.h> + +namespace Web::HTML::EventNames { + +// FIXME: Add media events https://html.spec.whatwg.org/multipage/media.html#mediaevents +// FIXME: Add app cache events https://html.spec.whatwg.org/multipage/offline.html#appcacheevents +// FIXME: Add drag and drop events https://html.spec.whatwg.org/multipage/dnd.html#dndevents + +#define ENUMERATE_HTML_EVENTS \ + __ENUMERATE_HTML_EVENT(abort) \ + __ENUMERATE_HTML_EVENT(DOMContentLoaded) \ + __ENUMERATE_HTML_EVENT(afterprint) \ + __ENUMERATE_HTML_EVENT(beforeprint) \ + __ENUMERATE_HTML_EVENT(beforeunload) \ + __ENUMERATE_HTML_EVENT(blur) \ + __ENUMERATE_HTML_EVENT(cancel) \ + __ENUMERATE_HTML_EVENT(change) \ + __ENUMERATE_HTML_EVENT(click) \ + __ENUMERATE_HTML_EVENT(close) \ + __ENUMERATE_HTML_EVENT(connect) \ + __ENUMERATE_HTML_EVENT(contextmenu) \ + __ENUMERATE_HTML_EVENT(copy) \ + __ENUMERATE_HTML_EVENT(cut) \ + __ENUMERATE_HTML_EVENT(error) \ + __ENUMERATE_HTML_EVENT(focus) \ + __ENUMERATE_HTML_EVENT(formdata) \ + __ENUMERATE_HTML_EVENT(hashchange) \ + __ENUMERATE_HTML_EVENT(input) \ + __ENUMERATE_HTML_EVENT(invalid) \ + __ENUMERATE_HTML_EVENT(languagechange) \ + __ENUMERATE_HTML_EVENT(load) \ + __ENUMERATE_HTML_EVENT(message) \ + __ENUMERATE_HTML_EVENT(messageerror) \ + __ENUMERATE_HTML_EVENT(offline) \ + __ENUMERATE_HTML_EVENT(online) \ + __ENUMERATE_HTML_EVENT(open) \ + __ENUMERATE_HTML_EVENT(pagehide) \ + __ENUMERATE_HTML_EVENT(pageshow) \ + __ENUMERATE_HTML_EVENT(paste) \ + __ENUMERATE_HTML_EVENT(popstate) \ + __ENUMERATE_HTML_EVENT(readystatechange) \ + __ENUMERATE_HTML_EVENT(rejectionhandled) \ + __ENUMERATE_HTML_EVENT(reset) \ + __ENUMERATE_HTML_EVENT(securitypolicyviolation) \ + __ENUMERATE_HTML_EVENT(select) \ + __ENUMERATE_HTML_EVENT(slotchange) \ + __ENUMERATE_HTML_EVENT(storage) \ + __ENUMERATE_HTML_EVENT(submit) \ + __ENUMERATE_HTML_EVENT(toggle) \ + __ENUMERATE_HTML_EVENT(unhandledrejection) \ + __ENUMERATE_HTML_EVENT(unload) + +#define __ENUMERATE_HTML_EVENT(name) extern FlyString name; +ENUMERATE_HTML_EVENTS +#undef __ENUMERATE_HTML_EVENT + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.cpp new file mode 100644 index 0000000000..f79debf4cc --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-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 <LibWeb/HTML/HTMLAnchorElement.h> + +namespace Web::HTML { + +HTMLAnchorElement::HTMLAnchorElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLAnchorElement::~HTMLAnchorElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.h b/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.h new file mode 100644 index 0000000000..02d7349116 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-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/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLAnchorElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLAnchorElementWrapper; + + HTMLAnchorElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLAnchorElement() override; + + String href() const { return attribute(HTML::AttributeNames::href); } + String target() const { return attribute(HTML::AttributeNames::target); } + + virtual bool is_focusable() const override { return has_attribute(HTML::AttributeNames::href); } +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.idl new file mode 100644 index 0000000000..829b9039ee --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.idl @@ -0,0 +1,16 @@ +interface HTMLAnchorElement : HTMLElement { + + [Reflect] attribute DOMString target; + [Reflect] attribute DOMString download; + [Reflect] attribute DOMString ping; + [Reflect] attribute DOMString rel; + [Reflect] attribute DOMString hreflang; + [Reflect] attribute DOMString type; + + [Reflect] attribute DOMString coords; + [Reflect] attribute DOMString charset; + [Reflect] attribute DOMString name; + [Reflect] attribute DOMString rev; + [Reflect] attribute DOMString shape; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.cpp new file mode 100644 index 0000000000..fe9c9a2147 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLAreaElement.h> + +namespace Web::HTML { + +HTMLAreaElement::HTMLAreaElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLAreaElement::~HTMLAreaElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.h b/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.h new file mode 100644 index 0000000000..29fa82ffa0 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLAreaElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLAreaElementWrapper; + + HTMLAreaElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLAreaElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.idl new file mode 100644 index 0000000000..7cd0058305 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.idl @@ -0,0 +1,5 @@ +interface HTMLAreaElement : HTMLElement { + + [Reflect=nohref] attribute boolean noHref; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.cpp new file mode 100644 index 0000000000..2653ebf944 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLAudioElement.h> + +namespace Web::HTML { + +HTMLAudioElement::HTMLAudioElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLMediaElement(document, qualified_name) +{ +} + +HTMLAudioElement::~HTMLAudioElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.h b/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.h new file mode 100644 index 0000000000..ce6a43b021 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLMediaElement.h> + +namespace Web::HTML { + +class HTMLAudioElement final : public HTMLMediaElement { +public: + using WrapperType = Bindings::HTMLAudioElementWrapper; + + HTMLAudioElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLAudioElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.idl new file mode 100644 index 0000000000..c56ca47938 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.idl @@ -0,0 +1,5 @@ +interface HTMLAudioElement : HTMLMediaElement { + + + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBRElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLBRElement.cpp new file mode 100644 index 0000000000..d3a0bc1b9e --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLBRElement.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-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 <LibWeb/HTML/HTMLBRElement.h> +#include <LibWeb/Layout/BreakNode.h> + +namespace Web::HTML { + +HTMLBRElement::HTMLBRElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLBRElement::~HTMLBRElement() +{ +} + +RefPtr<Layout::Node> HTMLBRElement::create_layout_node() +{ + return adopt(*new Layout::BreakNode(document(), *this)); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBRElement.h b/Userland/Libraries/LibWeb/HTML/HTMLBRElement.h new file mode 100644 index 0000000000..5e453c6ecb --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLBRElement.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-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/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLBRElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLBRElementWrapper; + + HTMLBRElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLBRElement() override; + + virtual RefPtr<Layout::Node> create_layout_node() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBRElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLBRElement.idl new file mode 100644 index 0000000000..a71383c890 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLBRElement.idl @@ -0,0 +1,5 @@ +interface HTMLBRElement : HTMLElement { + + [Reflect] attribute DOMString clear; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.cpp new file mode 100644 index 0000000000..975d0bddda --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLBaseElement.h> + +namespace Web::HTML { + +HTMLBaseElement::HTMLBaseElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLBaseElement::~HTMLBaseElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.h b/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.h new file mode 100644 index 0000000000..ce2be21e0d --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLBaseElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLBaseElementWrapper; + + HTMLBaseElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLBaseElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.idl new file mode 100644 index 0000000000..868e4a84a4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.idl @@ -0,0 +1,5 @@ +interface HTMLBaseElement : HTMLElement { + + [Reflect] attribute DOMString target; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBlinkElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLBlinkElement.cpp new file mode 100644 index 0000000000..911a8940bd --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLBlinkElement.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018-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 <LibCore/Timer.h> +#include <LibWeb/CSS/StyleProperties.h> +#include <LibWeb/CSS/StyleValue.h> +#include <LibWeb/HTML/HTMLBlinkElement.h> +#include <LibWeb/Layout/Node.h> + +namespace Web::HTML { + +HTMLBlinkElement::HTMLBlinkElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) + , m_timer(Core::Timer::construct()) +{ + m_timer->set_interval(500); + m_timer->on_timeout = [this] { blink(); }; + m_timer->start(); +} + +HTMLBlinkElement::~HTMLBlinkElement() +{ +} + +void HTMLBlinkElement::blink() +{ + if (!layout_node()) + return; + + layout_node()->set_visible(!layout_node()->is_visible()); + layout_node()->set_needs_display(); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBlinkElement.h b/Userland/Libraries/LibWeb/HTML/HTMLBlinkElement.h new file mode 100644 index 0000000000..e4c4c3421f --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLBlinkElement.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018-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 <LibCore/Forward.h> +#include <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLBlinkElement final : public HTMLElement { +public: + HTMLBlinkElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLBlinkElement() override; + +private: + void blink(); + + NonnullRefPtr<Core::Timer> m_timer; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.cpp new file mode 100644 index 0000000000..e6b7272f8d --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018-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 <LibWeb/CSS/StyleProperties.h> +#include <LibWeb/CSS/StyleValue.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/HTML/HTMLBodyElement.h> + +namespace Web::HTML { + +HTMLBodyElement::HTMLBodyElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLBodyElement::~HTMLBodyElement() +{ +} + +void HTMLBodyElement::apply_presentational_hints(CSS::StyleProperties& style) const +{ + for_each_attribute([&](auto& name, auto& value) { + if (name.equals_ignoring_case("bgcolor")) { + auto color = Color::from_string(value); + if (color.has_value()) + style.set_property(CSS::PropertyID::BackgroundColor, CSS::ColorStyleValue::create(color.value())); + } else if (name.equals_ignoring_case("text")) { + auto color = Color::from_string(value); + if (color.has_value()) + style.set_property(CSS::PropertyID::Color, CSS::ColorStyleValue::create(color.value())); + } else if (name.equals_ignoring_case("background")) { + ASSERT(m_background_style_value); + style.set_property(CSS::PropertyID::BackgroundImage, *m_background_style_value); + } + }); +} + +void HTMLBodyElement::parse_attribute(const FlyString& name, const String& value) +{ + HTMLElement::parse_attribute(name, value); + if (name.equals_ignoring_case("link")) { + auto color = Color::from_string(value); + if (color.has_value()) + document().set_link_color(color.value()); + } else if (name.equals_ignoring_case("alink")) { + auto color = Color::from_string(value); + if (color.has_value()) + document().set_active_link_color(color.value()); + } else if (name.equals_ignoring_case("vlink")) { + auto color = Color::from_string(value); + if (color.has_value()) + document().set_visited_link_color(color.value()); + } else if (name.equals_ignoring_case("background")) { + m_background_style_value = CSS::ImageStyleValue::create(document().complete_url(value), const_cast<DOM::Document&>(document())); + } +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.h b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.h new file mode 100644 index 0000000000..d8d41cbb7b --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-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/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLBodyElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLBodyElementWrapper; + + HTMLBodyElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLBodyElement() override; + + virtual void parse_attribute(const FlyString&, const String&) override; + virtual void apply_presentational_hints(CSS::StyleProperties&) const override; + +private: + RefPtr<CSS::ImageStyleValue> m_background_style_value; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.idl new file mode 100644 index 0000000000..dcd0b4d753 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.idl @@ -0,0 +1,10 @@ +interface HTMLBodyElement : HTMLElement { + + [LegacyNullToEmptyString, Reflect] attribute DOMString text; + [LegacyNullToEmptyString, Reflect] attribute DOMString link; + [LegacyNullToEmptyString, Reflect=vlink] attribute DOMString vLink; + [LegacyNullToEmptyString, Reflect=alink] attribute DOMString aLink; + [LegacyNullToEmptyString, Reflect=bgcolor] attribute DOMString bgColor; + [Reflect] attribute DOMString background; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.cpp new file mode 100644 index 0000000000..ec4925965b --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLButtonElement.h> + +namespace Web::HTML { + +HTMLButtonElement::HTMLButtonElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLButtonElement::~HTMLButtonElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.h b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.h new file mode 100644 index 0000000000..234656a97d --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLButtonElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLButtonElementWrapper; + + HTMLButtonElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLButtonElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.idl new file mode 100644 index 0000000000..7e50852e64 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.idl @@ -0,0 +1,8 @@ +interface HTMLButtonElement : HTMLElement { + + [Reflect=formnovalidate] attribute boolean formNoValidate; + [Reflect=formtarget] attribute DOMString formTarget; + [Reflect] attribute DOMString name; + [Reflect] attribute DOMString value; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp new file mode 100644 index 0000000000..d015225460 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018-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/Checked.h> +#include <LibGfx/Bitmap.h> +#include <LibWeb/CSS/StyleResolver.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/HTML/CanvasRenderingContext2D.h> +#include <LibWeb/HTML/HTMLCanvasElement.h> +#include <LibWeb/Layout/CanvasBox.h> + +namespace Web::HTML { + +static constexpr auto max_canvas_area = 16384 * 16384; + +HTMLCanvasElement::HTMLCanvasElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLCanvasElement::~HTMLCanvasElement() +{ +} + +unsigned HTMLCanvasElement::width() const +{ + return attribute(HTML::AttributeNames::width).to_uint().value_or(300); +} + +unsigned HTMLCanvasElement::height() const +{ + return attribute(HTML::AttributeNames::height).to_uint().value_or(150); +} + +RefPtr<Layout::Node> HTMLCanvasElement::create_layout_node() +{ + auto style = document().style_resolver().resolve_style(*this); + if (style->display() == CSS::Display::None) + return nullptr; + return adopt(*new Layout::CanvasBox(document(), *this, move(style))); +} + +CanvasRenderingContext2D* HTMLCanvasElement::get_context(String type) +{ + ASSERT(type == "2d"); + if (!m_context) + m_context = CanvasRenderingContext2D::create(*this); + return m_context; +} + +static Gfx::IntSize bitmap_size_for_canvas(const HTMLCanvasElement& canvas) +{ + auto width = canvas.width(); + auto height = canvas.height(); + + Checked<size_t> area = width; + area *= height; + + if (area.has_overflow()) { + dbgln("Refusing to create {}x{} canvas (overflow)", width, height); + return {}; + } + if (area.value() > max_canvas_area) { + dbgln("Refusing to create {}x{} canvas (exceeds maximum size)", width, height); + return {}; + } + return Gfx::IntSize(width, height); +} + +bool HTMLCanvasElement::create_bitmap() +{ + auto size = bitmap_size_for_canvas(*this); + if (size.is_empty()) { + m_bitmap = nullptr; + return false; + } + if (!m_bitmap || m_bitmap->size() != size) + m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, size); + return m_bitmap; +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.h b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.h new file mode 100644 index 0000000000..9616af98b7 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.h @@ -0,0 +1,58 @@ +/* + * 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 <AK/ByteBuffer.h> +#include <LibGfx/Forward.h> +#include <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLCanvasElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLCanvasElementWrapper; + + HTMLCanvasElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLCanvasElement() override; + + const Gfx::Bitmap* bitmap() const { return m_bitmap; } + Gfx::Bitmap* bitmap() { return m_bitmap; } + bool create_bitmap(); + + CanvasRenderingContext2D* get_context(String type); + + unsigned width() const; + unsigned height() const; + +private: + virtual RefPtr<Layout::Node> create_layout_node() override; + + RefPtr<Gfx::Bitmap> m_bitmap; + RefPtr<CanvasRenderingContext2D> m_context; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.idl new file mode 100644 index 0000000000..5a04895fb4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.idl @@ -0,0 +1,7 @@ +interface HTMLCanvasElement : HTMLElement { + + CanvasRenderingContext2D? getContext(DOMString contextId); + readonly attribute unsigned long width; + readonly attribute unsigned long height; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDListElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLDListElement.cpp new file mode 100644 index 0000000000..ce68845ca6 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDListElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLDListElement.h> + +namespace Web::HTML { + +HTMLDListElement::HTMLDListElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLDListElement::~HTMLDListElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDListElement.h b/Userland/Libraries/LibWeb/HTML/HTMLDListElement.h new file mode 100644 index 0000000000..cf0fe2a9c2 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDListElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLDListElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLDListElementWrapper; + + HTMLDListElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLDListElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDListElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLDListElement.idl new file mode 100644 index 0000000000..16b3b881ac --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDListElement.idl @@ -0,0 +1,5 @@ +interface HTMLDListElement : HTMLElement { + + [Reflect] attribute boolean compact; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDataElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLDataElement.cpp new file mode 100644 index 0000000000..1a23f48fd9 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDataElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLDataElement.h> + +namespace Web::HTML { + +HTMLDataElement::HTMLDataElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLDataElement::~HTMLDataElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDataElement.h b/Userland/Libraries/LibWeb/HTML/HTMLDataElement.h new file mode 100644 index 0000000000..21918958cd --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDataElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLDataElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLDataElementWrapper; + + HTMLDataElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLDataElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDataElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLDataElement.idl new file mode 100644 index 0000000000..4edce306fa --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDataElement.idl @@ -0,0 +1,5 @@ +interface HTMLDataElement : HTMLElement { + + [Reflect] attribute DOMString value; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.cpp new file mode 100644 index 0000000000..32bae03035 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLDataListElement.h> + +namespace Web::HTML { + +HTMLDataListElement::HTMLDataListElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLDataListElement::~HTMLDataListElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.h b/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.h new file mode 100644 index 0000000000..85bc7ccf7d --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLDataListElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLDataListElementWrapper; + + HTMLDataListElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLDataListElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.idl new file mode 100644 index 0000000000..c9383448e4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.idl @@ -0,0 +1,5 @@ +interface HTMLDataListElement : HTMLElement { + + + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.cpp new file mode 100644 index 0000000000..abcdf0801a --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLDetailsElement.h> + +namespace Web::HTML { + +HTMLDetailsElement::HTMLDetailsElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLDetailsElement::~HTMLDetailsElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.h b/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.h new file mode 100644 index 0000000000..2bdbf673f8 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLDetailsElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLDetailsElementWrapper; + + HTMLDetailsElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLDetailsElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.idl new file mode 100644 index 0000000000..2b0daa6c7f --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.idl @@ -0,0 +1,5 @@ +interface HTMLDetailsElement : HTMLElement { + + [Reflect] attribute boolean open; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.cpp new file mode 100644 index 0000000000..7437ca2261 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLDialogElement.h> + +namespace Web::HTML { + +HTMLDialogElement::HTMLDialogElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLDialogElement::~HTMLDialogElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.h b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.h new file mode 100644 index 0000000000..133f65ddbd --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLDialogElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLDialogElementWrapper; + + HTMLDialogElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLDialogElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.idl new file mode 100644 index 0000000000..0d37547ed3 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.idl @@ -0,0 +1,5 @@ +interface HTMLDialogElement : HTMLElement { + + [Reflect] attribute boolean open; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.cpp new file mode 100644 index 0000000000..53fee8b14a --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLDirectoryElement.h> + +namespace Web::HTML { + +HTMLDirectoryElement::HTMLDirectoryElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLDirectoryElement::~HTMLDirectoryElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.h b/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.h new file mode 100644 index 0000000000..39a9f5ae94 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.h @@ -0,0 +1,42 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +// NOTE: This element is marked as obsolete, but is still listed as required by the specification. +class HTMLDirectoryElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLDirectoryElementWrapper; + + HTMLDirectoryElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLDirectoryElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.idl new file mode 100644 index 0000000000..9adb6ad670 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.idl @@ -0,0 +1,5 @@ +interface HTMLDirectoryElement : HTMLElement { + + [Reflect] attribute boolean compact; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDivElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLDivElement.cpp new file mode 100644 index 0000000000..7392f8ec80 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDivElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLDivElement.h> + +namespace Web::HTML { + +HTMLDivElement::HTMLDivElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLDivElement::~HTMLDivElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDivElement.h b/Userland/Libraries/LibWeb/HTML/HTMLDivElement.h new file mode 100644 index 0000000000..eade002544 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDivElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLDivElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLDivElementWrapper; + + HTMLDivElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLDivElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDivElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLDivElement.idl new file mode 100644 index 0000000000..bb5cfba9b4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLDivElement.idl @@ -0,0 +1,5 @@ +interface HTMLDivElement : HTMLElement { + + [Reflect] attribute DOMString align; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp new file mode 100644 index 0000000000..eb5356a594 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2018-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/StringBuilder.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/HTML/HTMLAnchorElement.h> +#include <LibWeb/HTML/HTMLElement.h> +#include <LibWeb/Layout/BreakNode.h> +#include <LibWeb/Layout/TextNode.h> + +namespace Web::HTML { + +HTMLElement::HTMLElement(DOM::Document& document, const QualifiedName& qualified_name) + : Element(document, qualified_name) +{ +} + +HTMLElement::~HTMLElement() +{ +} + +HTMLElement::ContentEditableState HTMLElement::content_editable_state() const +{ + auto contenteditable = attribute(HTML::AttributeNames::contenteditable); + // "true" and the empty string map to the "true" state. + if ((!contenteditable.is_null() && contenteditable.is_empty()) || contenteditable.equals_ignoring_case("true")) + return ContentEditableState::True; + // "false" maps to the "false" state. + if (contenteditable.equals_ignoring_case("false")) + return ContentEditableState::False; + // "inherit", an invalid value, and a missing value all map to the "inherit" state. + return ContentEditableState::Inherit; +} + +bool HTMLElement::is_editable() const +{ + switch (content_editable_state()) { + case ContentEditableState::True: + return true; + case ContentEditableState::False: + return false; + case ContentEditableState::Inherit: + return parent() && parent()->is_editable(); + default: + ASSERT_NOT_REACHED(); + } +} + +String HTMLElement::content_editable() const +{ + switch (content_editable_state()) { + case ContentEditableState::True: + return "true"; + case ContentEditableState::False: + return "false"; + case ContentEditableState::Inherit: + return "inherit"; + default: + ASSERT_NOT_REACHED(); + } +} + +void HTMLElement::set_content_editable(const String& content_editable) +{ + if (content_editable.equals_ignoring_case("inherit")) { + remove_attribute(HTML::AttributeNames::contenteditable); + return; + } + if (content_editable.equals_ignoring_case("true")) { + set_attribute(HTML::AttributeNames::contenteditable, "true"); + return; + } + if (content_editable.equals_ignoring_case("false")) { + set_attribute(HTML::AttributeNames::contenteditable, "false"); + return; + } + // FIXME: otherwise the attribute setter must throw a "SyntaxError" DOMException. +} + +void HTMLElement::set_inner_text(StringView text) +{ + remove_all_children(); + append_child(document().create_text_node(text)); + + set_needs_style_update(true); + document().invalidate_layout(); +} + +String HTMLElement::inner_text() +{ + StringBuilder builder; + + // innerText for element being rendered takes visibility into account, so force a layout and then walk the layout tree. + document().update_layout(); + if (!layout_node()) + return text_content(); + + Function<void(const Layout::Node&)> recurse = [&](auto& node) { + for (auto* child = node.first_child(); child; child = child->next_sibling()) { + if (is<Layout::TextNode>(child)) + builder.append(downcast<Layout::TextNode>(*child).text_for_rendering()); + if (is<Layout::BreakNode>(child)) + builder.append('\n'); + recurse(*child); + } + }; + recurse(*layout_node()); + + return builder.to_string(); +} + +bool HTMLElement::cannot_navigate() const +{ + // FIXME: Return true if element's node document is not fully active + return !is<HTML::HTMLAnchorElement>(this) && !is_connected(); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.h b/Userland/Libraries/LibWeb/HTML/HTMLElement.h new file mode 100644 index 0000000000..ee602a62b5 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-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/DOM/Element.h> + +namespace Web::HTML { + +class HTMLElement : public DOM::Element { +public: + using WrapperType = Bindings::HTMLElementWrapper; + + HTMLElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLElement() override; + + String title() const { return attribute(HTML::AttributeNames::title); } + + virtual bool is_editable() const final; + String content_editable() const; + void set_content_editable(const String&); + + String inner_text(); + void set_inner_text(StringView); + + bool cannot_navigate() const; + +private: + enum class ContentEditableState { + True, + False, + Inherit, + }; + ContentEditableState content_editable_state() const; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLElement.idl new file mode 100644 index 0000000000..6b5de34cdb --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.idl @@ -0,0 +1,11 @@ +interface HTMLElement : Element { + + [Reflect] attribute DOMString title; + [Reflect] attribute DOMString lang; + + [Reflect] attribute boolean hidden; + + attribute DOMString contentEditable; + + [LegacyNullToEmptyString] attribute DOMString innerText; +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.cpp new file mode 100644 index 0000000000..1ec87b0fd0 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLEmbedElement.h> + +namespace Web::HTML { + +HTMLEmbedElement::HTMLEmbedElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLEmbedElement::~HTMLEmbedElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.h b/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.h new file mode 100644 index 0000000000..1ceaaae634 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLEmbedElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLEmbedElementWrapper; + + HTMLEmbedElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLEmbedElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.idl new file mode 100644 index 0000000000..5eb48b3509 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.idl @@ -0,0 +1,11 @@ +interface HTMLEmbedElement : HTMLElement { + + [Reflect] attribute DOMString src; + [Reflect] attribute DOMString type; + [Reflect] attribute DOMString width; + [Reflect] attribute DOMString height; + + [Reflect] attribute DOMString align; + [Reflect] attribute DOMString name; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.cpp new file mode 100644 index 0000000000..6a6c5bf87e --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLFieldSetElement.h> + +namespace Web::HTML { + +HTMLFieldSetElement::HTMLFieldSetElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLFieldSetElement::~HTMLFieldSetElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.h b/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.h new file mode 100644 index 0000000000..b9ffcf43be --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.h @@ -0,0 +1,47 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLFieldSetElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLFieldSetElementWrapper; + + HTMLFieldSetElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLFieldSetElement() override; + + const String& type() const + { + static String fieldset = "fieldset"; + return fieldset; + } +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.idl new file mode 100644 index 0000000000..4901c8aae8 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.idl @@ -0,0 +1,5 @@ +interface HTMLFieldSetElement : HTMLElement { + + readonly attribute DOMString type; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFontElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLFontElement.cpp new file mode 100644 index 0000000000..50ef0e37f5 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLFontElement.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018-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 <LibWeb/CSS/StyleProperties.h> +#include <LibWeb/CSS/StyleValue.h> +#include <LibWeb/HTML/HTMLFontElement.h> + +namespace Web::HTML { + +HTMLFontElement::HTMLFontElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLFontElement::~HTMLFontElement() +{ +} + +void HTMLFontElement::apply_presentational_hints(CSS::StyleProperties& style) const +{ + for_each_attribute([&](auto& name, auto& value) { + if (name.equals_ignoring_case("color")) { + auto color = Color::from_string(value); + if (color.has_value()) + style.set_property(CSS::PropertyID::Color, CSS::ColorStyleValue::create(color.value())); + } + }); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFontElement.h b/Userland/Libraries/LibWeb/HTML/HTMLFontElement.h new file mode 100644 index 0000000000..d414abc5a8 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLFontElement.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-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/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLFontElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLFontElementWrapper; + + HTMLFontElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLFontElement() override; + + virtual void apply_presentational_hints(CSS::StyleProperties&) const override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFontElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLFontElement.idl new file mode 100644 index 0000000000..578e313ed0 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLFontElement.idl @@ -0,0 +1,7 @@ +interface HTMLFontElement : HTMLElement { + + [LegacyNullToEmptyString, Reflect] attribute DOMString color; + [Reflect] attribute DOMString face; + [Reflect] attribute DOMString size; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFormElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.cpp new file mode 100644 index 0000000000..b632dab482 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2018-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/StringBuilder.h> +#include <LibWeb/HTML/EventNames.h> +#include <LibWeb/HTML/HTMLFormElement.h> +#include <LibWeb/HTML/HTMLInputElement.h> +#include <LibWeb/HTML/SubmitEvent.h> +#include <LibWeb/InProcessWebView.h> +#include <LibWeb/Page/Frame.h> +#include <LibWeb/URLEncoder.h> + +namespace Web::HTML { + +HTMLFormElement::HTMLFormElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLFormElement::~HTMLFormElement() +{ +} + +void HTMLFormElement::submit_form(RefPtr<HTMLElement> submitter, bool from_submit_binding) +{ + if (cannot_navigate()) + return; + + if (action().is_null()) { + dbgln("Unsupported form action ''"); + return; + } + + auto effective_method = method().to_lowercase(); + + if (effective_method == "dialog") { + dbgln("Failed to submit form: Unsupported form method '{}'", method()); + return; + } + + if (effective_method != "get" && effective_method != "post") { + effective_method = "get"; + } + + if (!from_submit_binding) { + if (m_firing_submission_events) + return; + + m_firing_submission_events = true; + + // FIXME: If the submitter element's no-validate state is false... + + RefPtr<HTMLElement> submitter_button; + + if (submitter != this) + submitter_button = submitter; + + auto submit_event = SubmitEvent::create(EventNames::submit, submitter_button); + submit_event->set_bubbles(true); + submit_event->set_cancelable(true); + bool continue_ = dispatch_event(submit_event); + + m_firing_submission_events = false; + + if (!continue_) + return; + + // This is checked again because arbitrary JS may have run when handling submit, + // which may have changed the result. + if (cannot_navigate()) + return; + } + + URL url(document().complete_url(action())); + + if (!url.is_valid()) { + dbgln("Failed to submit form: Invalid URL: {}", action()); + return; + } + + if (url.protocol() == "file") { + if (document().url().protocol() != "file") { + dbgln("Failed to submit form: Security violation: {} may not submit to {}", document().url(), url); + return; + } + if (effective_method != "get") { + dbgln("Failed to submit form: Unsupported form method '{}' for URL: {}", method(), url); + return; + } + } else if (url.protocol() != "http" && url.protocol() != "https") { + dbgln("Failed to submit form: Unsupported protocol for URL: {}", url); + return; + } + + Vector<URLQueryParam> parameters; + + for_each_in_subtree_of_type<HTMLInputElement>([&](auto& node) { + auto& input = downcast<HTMLInputElement>(node); + if (!input.name().is_null() && (input.type() != "submit" || &input == submitter)) + parameters.append({ input.name(), input.value() }); + return IterationDecision::Continue; + }); + + if (effective_method == "get") { + url.set_query(urlencode(parameters)); + } + + LoadRequest request; + request.set_url(url); + + if (effective_method == "post") { + auto body = urlencode(parameters).to_byte_buffer(); + request.set_method("POST"); + request.set_header("Content-Type", "application/x-www-form-urlencoded"); + request.set_header("Content-Length", String::number(body.size())); + request.set_body(body); + } + + if (auto* page = document().page()) + page->load(request); +} + +void HTMLFormElement::submit() +{ + submit_form(this, true); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFormElement.h b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.h new file mode 100644 index 0000000000..8f90e8f3f7 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018-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/HTML/HTMLElement.h> +#include <LibWeb/HTML/HTMLInputElement.h> + +namespace Web::HTML { + +class HTMLFormElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLFormElementWrapper; + + HTMLFormElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLFormElement() override; + + String action() const { return attribute(HTML::AttributeNames::action); } + String method() const { return attribute(HTML::AttributeNames::method); } + + void submit_form(RefPtr<HTMLElement> submitter, bool from_submit_binding = false); + + // NOTE: This is for the JS bindings. Use submit_form instead. + void submit(); + +private: + bool m_firing_submission_events { false }; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFormElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.idl new file mode 100644 index 0000000000..81d4fe37f4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.idl @@ -0,0 +1,10 @@ +interface HTMLFormElement : HTMLElement { + + [Reflect] attribute DOMString name; + [Reflect] attribute DOMString rel; + [Reflect=accept-charset] attribute DOMString acceptCharset; + [Reflect=novalidate] attribute boolean noValidate; + + undefined submit(); + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.cpp new file mode 100644 index 0000000000..ba2e0fb470 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLFrameElement.h> + +namespace Web::HTML { + +HTMLFrameElement::HTMLFrameElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLFrameElement::~HTMLFrameElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.h b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.h new file mode 100644 index 0000000000..7541bc80b5 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.h @@ -0,0 +1,42 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +// NOTE: This element is marked as obsolete, but is still listed as required by the specification. +class HTMLFrameElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLFrameElementWrapper; + + HTMLFrameElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLFrameElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.idl new file mode 100644 index 0000000000..e1092ce79e --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.idl @@ -0,0 +1,9 @@ +interface HTMLFrameElement : HTMLElement { + + [Reflect] attribute DOMString name; + [Reflect] attribute DOMString scrolling; + [Reflect] attribute DOMString src; + [Reflect=frameborder] attribute DOMString frameBorder; + [Reflect=longdesc] attribute DOMString longDesc; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.cpp new file mode 100644 index 0000000000..128da69ccf --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLFrameSetElement.h> + +namespace Web::HTML { + +HTMLFrameSetElement::HTMLFrameSetElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLFrameSetElement::~HTMLFrameSetElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.h b/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.h new file mode 100644 index 0000000000..0c2af01678 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.h @@ -0,0 +1,42 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +// NOTE: This element is marked as obsolete, but is still listed as required by the specification. +class HTMLFrameSetElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLFrameSetElementWrapper; + + HTMLFrameSetElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLFrameSetElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.idl new file mode 100644 index 0000000000..abf5699ec4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.idl @@ -0,0 +1,6 @@ +interface HTMLFrameSetElement : HTMLElement { + + [Reflect] attribute DOMString cols; + [Reflect] attribute DOMString rows; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHRElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLHRElement.cpp new file mode 100644 index 0000000000..82574cf789 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLHRElement.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-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 <LibWeb/HTML/HTMLHRElement.h> + +namespace Web::HTML { + +HTMLHRElement::HTMLHRElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLHRElement::~HTMLHRElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHRElement.h b/Userland/Libraries/LibWeb/HTML/HTMLHRElement.h new file mode 100644 index 0000000000..e8a23c1a19 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLHRElement.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-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/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLHRElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLHRElementWrapper; + + HTMLHRElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLHRElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHRElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLHRElement.idl new file mode 100644 index 0000000000..5cfd1e700e --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLHRElement.idl @@ -0,0 +1,9 @@ +interface HTMLHRElement : HTMLElement { + + [Reflect] attribute DOMString align; + [Reflect] attribute DOMString color; + [Reflect=noshade] attribute boolean noShade; + [Reflect] attribute DOMString size; + [Reflect] attribute DOMString width; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.cpp new file mode 100644 index 0000000000..a17ead858b --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-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 <LibWeb/HTML/HTMLHeadElement.h> + +namespace Web::HTML { + +HTMLHeadElement::HTMLHeadElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLHeadElement::~HTMLHeadElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.h b/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.h new file mode 100644 index 0000000000..d378e1af77 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-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/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLHeadElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLHeadElementWrapper; + + HTMLHeadElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLHeadElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.idl new file mode 100644 index 0000000000..8730e7c1e4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.idl @@ -0,0 +1,5 @@ +interface HTMLHeadElement : HTMLElement { + + + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.cpp new file mode 100644 index 0000000000..b899c873a5 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-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 <LibWeb/HTML/HTMLHeadingElement.h> + +namespace Web::HTML { + +HTMLHeadingElement::HTMLHeadingElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLHeadingElement::~HTMLHeadingElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.h b/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.h new file mode 100644 index 0000000000..654b75baf9 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-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/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLHeadingElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLHeadingElementWrapper; + + HTMLHeadingElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLHeadingElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.idl new file mode 100644 index 0000000000..fa3d5a4ed7 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.idl @@ -0,0 +1,5 @@ +interface HTMLHeadingElement : HTMLElement { + + [Reflect] attribute DOMString align; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.cpp new file mode 100644 index 0000000000..ab1233f739 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-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 <LibWeb/HTML/HTMLHtmlElement.h> + +namespace Web::HTML { + +HTMLHtmlElement::HTMLHtmlElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLHtmlElement::~HTMLHtmlElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.h b/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.h new file mode 100644 index 0000000000..3e34c52f6c --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-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/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLHtmlElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLHtmlElementWrapper; + + HTMLHtmlElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLHtmlElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.idl new file mode 100644 index 0000000000..dad97564c8 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.idl @@ -0,0 +1,5 @@ +interface HTMLHtmlElement : HTMLElement { + + [Reflect] attribute DOMString version; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.cpp new file mode 100644 index 0000000000..e59818ba82 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.cpp @@ -0,0 +1,108 @@ +/* + * 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 <LibGUI/Button.h> +#include <LibGUI/TextBox.h> +#include <LibWeb/Bindings/WindowObject.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/DOM/Window.h> +#include <LibWeb/Dump.h> +#include <LibWeb/HTML/EventNames.h> +#include <LibWeb/HTML/HTMLFormElement.h> +#include <LibWeb/HTML/HTMLIFrameElement.h> +#include <LibWeb/HTML/Parser/HTMLDocumentParser.h> +#include <LibWeb/InProcessWebView.h> +#include <LibWeb/Layout/FrameBox.h> +#include <LibWeb/Loader/ResourceLoader.h> +#include <LibWeb/Origin.h> +#include <LibWeb/Page/Frame.h> + +namespace Web::HTML { + +HTMLIFrameElement::HTMLIFrameElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ + ASSERT(document.frame()); + m_content_frame = Frame::create_subframe(*this, document.frame()->main_frame()); +} + +HTMLIFrameElement::~HTMLIFrameElement() +{ +} + +RefPtr<Layout::Node> HTMLIFrameElement::create_layout_node() +{ + auto style = document().style_resolver().resolve_style(*this); + return adopt(*new Layout::FrameBox(document(), *this, move(style))); +} + +void HTMLIFrameElement::parse_attribute(const FlyString& name, const String& value) +{ + HTMLElement::parse_attribute(name, value); + if (name == HTML::AttributeNames::src) + load_src(value); +} + +void HTMLIFrameElement::load_src(const String& value) +{ + auto url = document().complete_url(value); + if (!url.is_valid()) { + dbg() << "iframe failed to load URL: Invalid URL: " << value; + return; + } + if (url.protocol() == "file" && document().origin().protocol() != "file") { + dbg() << "iframe failed to load URL: Security violation: " << document().url() << " may not load " << url; + return; + } + + dbg() << "Loading iframe document from " << value; + m_content_frame->loader().load(url, FrameLoader::Type::IFrame); +} + +Origin HTMLIFrameElement::content_origin() const +{ + if (!m_content_frame || !m_content_frame->document()) + return {}; + return m_content_frame->document()->origin(); +} + +bool HTMLIFrameElement::may_access_from_origin(const Origin& origin) const +{ + return origin.is_same(content_origin()); +} + +const DOM::Document* HTMLIFrameElement::content_document() const +{ + return m_content_frame ? m_content_frame->document() : nullptr; +} + +void HTMLIFrameElement::content_frame_did_load(Badge<FrameLoader>) +{ + dispatch_event(DOM::Event::create(EventNames::load)); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.h b/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.h new file mode 100644 index 0000000000..0237ea3c91 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.h @@ -0,0 +1,60 @@ +/* + * 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/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLIFrameElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLIFrameElementWrapper; + + HTMLIFrameElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLIFrameElement() override; + + virtual RefPtr<Layout::Node> create_layout_node() override; + + Frame* content_frame() { return m_content_frame; } + const Frame* content_frame() const { return m_content_frame; } + + const DOM::Document* content_document() const; + + Origin content_origin() const; + bool may_access_from_origin(const Origin&) const; + + void content_frame_did_load(Badge<FrameLoader>); + +private: + virtual void parse_attribute(const FlyString& name, const String& value) override; + + void load_src(const String&); + + RefPtr<Frame> m_content_frame; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.idl new file mode 100644 index 0000000000..a219b66071 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.idl @@ -0,0 +1,19 @@ +interface HTMLIFrameElement : HTMLElement { + + [Reflect] attribute DOMString src; + [Reflect] attribute DOMString srcdoc; + [Reflect] attribute DOMString name; + [Reflect] attribute DOMString allow; + [Reflect] attribute DOMString width; + [Reflect] attribute DOMString height; + [Reflect=allowfullscreen] attribute boolean allowFullscreen; + + [ReturnNullIfCrossOrigin] readonly attribute Document? contentDocument; + + [Reflect] attribute DOMString align; + [Reflect] attribute DOMString scrolling; + [Reflect=frameborder] attribute DOMString frameBorder; + + [LegacyNullToEmptyString, Reflect=marginheight] attribute DOMString marginHeight; + [LegacyNullToEmptyString, Reflect=marginwidth] attribute DOMString marginWidth; +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp new file mode 100644 index 0000000000..61b99f0ffa --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018-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 <LibGfx/Bitmap.h> +#include <LibGfx/ImageDecoder.h> +#include <LibWeb/CSS/Parser/CSSParser.h> +#include <LibWeb/CSS/StyleResolver.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/HTML/EventNames.h> +#include <LibWeb/HTML/HTMLImageElement.h> +#include <LibWeb/Layout/ImageBox.h> +#include <LibWeb/Loader/ResourceLoader.h> + +namespace Web::HTML { + +HTMLImageElement::HTMLImageElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ + m_image_loader.on_load = [this] { + this->document().update_layout(); + dispatch_event(DOM::Event::create(EventNames::load)); + }; + + m_image_loader.on_fail = [this] { + dbgln("HTMLImageElement: Resource did fail: {}", src()); + this->document().update_layout(); + dispatch_event(DOM::Event::create(EventNames::error)); + }; + + m_image_loader.on_animate = [this] { + if (layout_node()) + layout_node()->set_needs_display(); + }; +} + +HTMLImageElement::~HTMLImageElement() +{ +} + +void HTMLImageElement::apply_presentational_hints(CSS::StyleProperties& style) const +{ + for_each_attribute([&](auto& name, auto& value) { + if (name == HTML::AttributeNames::width) { + if (auto parsed_value = parse_html_length(document(), value)) { + style.set_property(CSS::PropertyID::Width, parsed_value.release_nonnull()); + } + } else if (name == HTML::AttributeNames::height) { + if (auto parsed_value = parse_html_length(document(), value)) { + style.set_property(CSS::PropertyID::Height, parsed_value.release_nonnull()); + } + } + }); +} + +void HTMLImageElement::parse_attribute(const FlyString& name, const String& value) +{ + HTMLElement::parse_attribute(name, value); + + if (name == HTML::AttributeNames::src) + m_image_loader.load(document().complete_url(value)); +} + +RefPtr<Layout::Node> HTMLImageElement::create_layout_node() +{ + auto style = document().style_resolver().resolve_style(*this); + if (style->display() == CSS::Display::None) + return nullptr; + return adopt(*new Layout::ImageBox(document(), *this, move(style), m_image_loader)); +} + +const Gfx::Bitmap* HTMLImageElement::bitmap() const +{ + return m_image_loader.bitmap(); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.h b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.h new file mode 100644 index 0000000000..7cc97f1983 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-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 <AK/ByteBuffer.h> +#include <AK/OwnPtr.h> +#include <LibGfx/Forward.h> +#include <LibWeb/HTML/HTMLElement.h> +#include <LibWeb/Loader/ImageLoader.h> + +namespace Web::HTML { + +class HTMLImageElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLImageElementWrapper; + + HTMLImageElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLImageElement() override; + + virtual void parse_attribute(const FlyString& name, const String& value) override; + + String alt() const { return attribute(HTML::AttributeNames::alt); } + String src() const { return attribute(HTML::AttributeNames::src); } + + const Gfx::Bitmap* bitmap() const; + +private: + virtual void apply_presentational_hints(CSS::StyleProperties&) const override; + + void animate(); + + virtual RefPtr<Layout::Node> create_layout_node() override; + + ImageLoader m_image_loader; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.idl new file mode 100644 index 0000000000..d0d4330a9a --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.idl @@ -0,0 +1,14 @@ +interface HTMLImageElement : HTMLElement { + + [Reflect] attribute DOMString src; + [Reflect] attribute DOMString alt; + [Reflect] attribute DOMString srcset; + [Reflect] attribute DOMString sizes; + [Reflect=usemap] attribute DOMString useMap; + [Reflect=ismap] attribute boolean isMap; + + [Reflect] attribute DOMString name; + [Reflect] attribute DOMString align; + [LegacyNullToEmptyString, Reflect] attribute DOMString border; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp new file mode 100644 index 0000000000..c5654c3530 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018-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 <LibGUI/Button.h> +#include <LibGUI/TextBox.h> +#include <LibGfx/FontDatabase.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/HTML/EventNames.h> +#include <LibWeb/HTML/HTMLFormElement.h> +#include <LibWeb/HTML/HTMLInputElement.h> +#include <LibWeb/InProcessWebView.h> +#include <LibWeb/Layout/ButtonBox.h> +#include <LibWeb/Layout/CheckBox.h> +#include <LibWeb/Layout/WidgetBox.h> +#include <LibWeb/Page/Frame.h> + +namespace Web::HTML { + +HTMLInputElement::HTMLInputElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLInputElement::~HTMLInputElement() +{ +} + +void HTMLInputElement::did_click_button(Badge<Layout::ButtonBox>) +{ + // FIXME: This should be a PointerEvent. + dispatch_event(DOM::Event::create(EventNames::click)); + + if (type().equals_ignoring_case("submit")) { + if (auto* form = first_ancestor_of_type<HTMLFormElement>()) { + form->submit_form(this); + } + return; + } +} + +RefPtr<Layout::Node> HTMLInputElement::create_layout_node() +{ + ASSERT(document().page()); + auto& page = *document().page(); + auto& page_view = const_cast<InProcessWebView&>(static_cast<const InProcessWebView&>(page.client())); + + if (type() == "hidden") + return nullptr; + + auto style = document().style_resolver().resolve_style(*this); + if (style->display() == CSS::Display::None) + return nullptr; + + if (type().equals_ignoring_case("submit") || type().equals_ignoring_case("button")) + return adopt(*new Layout::ButtonBox(document(), *this, move(style))); + + if (type() == "checkbox") + return adopt(*new Layout::CheckBox(document(), *this, move(style))); + + auto& text_box = page_view.add<GUI::TextBox>(); + text_box.set_text(value()); + text_box.on_change = [this] { + auto& widget = downcast<Layout::WidgetBox>(layout_node())->widget(); + const_cast<HTMLInputElement*>(this)->set_attribute(HTML::AttributeNames::value, static_cast<const GUI::TextBox&>(widget).text()); + }; + int text_width = Gfx::FontDatabase::default_font().width(value()); + auto size_value = attribute(HTML::AttributeNames::size); + if (!size_value.is_null()) { + auto size = size_value.to_uint(); + if (size.has_value()) + text_width = Gfx::FontDatabase::default_font().glyph_width('x') * size.value(); + } + text_box.set_relative_rect(0, 0, text_width + 20, 20); + return adopt(*new Layout::WidgetBox(document(), *this, text_box)); +} + +void HTMLInputElement::set_checked(bool checked) +{ + if (m_checked == checked) + return; + m_checked = checked; + if (layout_node()) + layout_node()->set_needs_display(); + + dispatch_event(DOM::Event::create(EventNames::change)); +} + +bool HTMLInputElement::enabled() const +{ + return !has_attribute(HTML::AttributeNames::disabled); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h new file mode 100644 index 0000000000..b887118263 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018-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/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLInputElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLInputElementWrapper; + + HTMLInputElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLInputElement() override; + + virtual RefPtr<Layout::Node> create_layout_node() override; + + String type() const { return attribute(HTML::AttributeNames::type); } + String value() const { return attribute(HTML::AttributeNames::value); } + String name() const { return attribute(HTML::AttributeNames::name); } + + bool checked() const { return m_checked; } + void set_checked(bool); + + bool enabled() const; + + void did_click_button(Badge<Layout::ButtonBox>); + +private: + bool m_checked { false }; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl new file mode 100644 index 0000000000..037b65c280 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl @@ -0,0 +1,27 @@ +interface HTMLInputElement : HTMLElement { + + [Reflect] attribute DOMString accept; + [Reflect] attribute DOMString alt; + [Reflect] attribute DOMString max; + [Reflect] attribute DOMString min; + [Reflect] attribute DOMString pattern; + [Reflect] attribute DOMString placeholder; + [Reflect] attribute DOMString src; + [Reflect] attribute DOMString step; + [Reflect=dirname] attribute DOMString dirName; + [Reflect=value] attribute DOMString defaultValue; + + attribute boolean checked; + + [Reflect] attribute boolean disabled; + [Reflect=checked] attribute boolean defaultChecked; + [Reflect=formnovalidate] attribute boolean formNoValidate; + [Reflect=formtarget] attribute DOMString formTarget; + [Reflect] attribute boolean multiple; + [Reflect=readonly] attribute boolean readOnly; + [Reflect] attribute boolean required; + + [Reflect] attribute DOMString align; + [Reflect=usemap] attribute DOMString useMap; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLIElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.cpp new file mode 100644 index 0000000000..fbdb4d0444 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLLIElement.h> + +namespace Web::HTML { + +HTMLLIElement::HTMLLIElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLLIElement::~HTMLLIElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLIElement.h b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.h new file mode 100644 index 0000000000..b0113aef9c --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLLIElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLLIElementWrapper; + + HTMLLIElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLLIElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLIElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.idl new file mode 100644 index 0000000000..9330aa88b1 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.idl @@ -0,0 +1,5 @@ +interface HTMLLIElement : HTMLElement { + + [Reflect] attribute DOMString type; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.cpp new file mode 100644 index 0000000000..766c64eecd --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLLabelElement.h> + +namespace Web::HTML { + +HTMLLabelElement::HTMLLabelElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLLabelElement::~HTMLLabelElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.h b/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.h new file mode 100644 index 0000000000..210e366ee0 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLLabelElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLLabelElementWrapper; + + HTMLLabelElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLLabelElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.idl new file mode 100644 index 0000000000..bbddd2052f --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.idl @@ -0,0 +1,5 @@ +interface HTMLLabelElement : HTMLElement { + + [Reflect=for] attribute DOMString htmlFor; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.cpp new file mode 100644 index 0000000000..aa2bbc2049 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLLegendElement.h> + +namespace Web::HTML { + +HTMLLegendElement::HTMLLegendElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLLegendElement::~HTMLLegendElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.h b/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.h new file mode 100644 index 0000000000..02a8b24585 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLLegendElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLLegendElementWrapper; + + HTMLLegendElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLLegendElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.idl new file mode 100644 index 0000000000..49ee03cd21 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.idl @@ -0,0 +1,5 @@ +interface HTMLLegendElement : HTMLElement { + + [Reflect] attribute DOMString align; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.cpp new file mode 100644 index 0000000000..323c642d61 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018-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/ByteBuffer.h> +#include <AK/URL.h> +#include <LibCore/File.h> +#include <LibWeb/CSS/Parser/CSSParser.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/HTML/HTMLLinkElement.h> +#include <LibWeb/Loader/ResourceLoader.h> + +namespace Web::HTML { + +HTMLLinkElement::HTMLLinkElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLLinkElement::~HTMLLinkElement() +{ +} + +void HTMLLinkElement::inserted_into(Node& node) +{ + HTMLElement::inserted_into(node); + + if (m_relationship & Relationship::Stylesheet && !(m_relationship & Relationship::Alternate)) + load_stylesheet(document().complete_url(href())); +} + +void HTMLLinkElement::resource_did_fail() +{ +} + +void HTMLLinkElement::resource_did_load() +{ + ASSERT(resource()); + if (!resource()->has_encoded_data()) + return; + + dbg() << "HTMLLinkElement: Resource did load, looks good! " << href(); + + auto sheet = parse_css(CSS::ParsingContext(document()), resource()->encoded_data()); + if (!sheet) { + dbg() << "HTMLLinkElement: Failed to parse stylesheet: " << href(); + return; + } + + // Transfer the rules from the successfully parsed sheet into the sheet we've already inserted. + m_style_sheet->rules() = sheet->rules(); + + document().update_style(); +} + +void HTMLLinkElement::load_stylesheet(const URL& url) +{ + // First insert an empty style sheet in the document sheet list. + // There's probably a nicer way to do this, but this ensures that sheets are in document order. + m_style_sheet = CSS::StyleSheet::create({}); + document().style_sheets().add_sheet(*m_style_sheet); + + LoadRequest request; + request.set_url(url); + set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, request)); +} + +void HTMLLinkElement::parse_attribute(const FlyString& name, const String& value) +{ + if (name == HTML::AttributeNames::rel) { + m_relationship = 0; + auto parts = value.split_view(' '); + for (auto& part : parts) { + if (part == "stylesheet") + m_relationship |= Relationship::Stylesheet; + else if (part == "alternate") + m_relationship |= Relationship::Alternate; + } + } +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.h b/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.h new file mode 100644 index 0000000000..ac91ecbe21 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018-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/HTML/HTMLElement.h> +#include <LibWeb/Loader/Resource.h> + +namespace Web::HTML { + +class HTMLLinkElement final + : public HTMLElement + , public ResourceClient { +public: + using WrapperType = Bindings::HTMLLinkElementWrapper; + + HTMLLinkElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLLinkElement() override; + + virtual void inserted_into(Node&) override; + + String rel() const { return attribute(HTML::AttributeNames::rel); } + String type() const { return attribute(HTML::AttributeNames::type); } + String href() const { return attribute(HTML::AttributeNames::href); } + +private: + // ^ResourceClient + virtual void resource_did_fail() override; + virtual void resource_did_load() override; + + void parse_attribute(const FlyString&, const String&) override; + + void load_stylesheet(const URL&); + + struct Relationship { + enum { + Alternate = 1 << 0, + Stylesheet = 1 << 1, + }; + }; + + unsigned m_relationship { 0 }; + RefPtr<CSS::StyleSheet> m_style_sheet; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.idl new file mode 100644 index 0000000000..e1999c74b8 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.idl @@ -0,0 +1,17 @@ +interface HTMLLinkElement : HTMLElement { + + [Reflect] attribute DOMString href; + [Reflect] attribute DOMString hreflang; + [Reflect] attribute DOMString integrity; + [Reflect] attribute DOMString media; + [Reflect] attribute DOMString rel; + [Reflect] attribute DOMString type; + [Reflect=imagesrcset] attribute DOMString imageSrcset; + [Reflect=imagesizes] attribute DOMString imageSizes; + [Reflect] attribute boolean disabled; + + [Reflect] attribute DOMString charset; + [Reflect] attribute DOMString rev; + [Reflect] attribute DOMString target; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMapElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLMapElement.cpp new file mode 100644 index 0000000000..c85b871c71 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMapElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLMapElement.h> + +namespace Web::HTML { + +HTMLMapElement::HTMLMapElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLMapElement::~HTMLMapElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMapElement.h b/Userland/Libraries/LibWeb/HTML/HTMLMapElement.h new file mode 100644 index 0000000000..23b59ce6fc --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMapElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLMapElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLMapElementWrapper; + + HTMLMapElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLMapElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMapElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLMapElement.idl new file mode 100644 index 0000000000..636a96f8a5 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMapElement.idl @@ -0,0 +1,5 @@ +interface HTMLMapElement : HTMLElement { + + [Reflect] attribute DOMString name; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.cpp new file mode 100644 index 0000000000..02e5e2ec6c --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLMarqueeElement.h> + +namespace Web::HTML { + +HTMLMarqueeElement::HTMLMarqueeElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLMarqueeElement::~HTMLMarqueeElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.h b/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.h new file mode 100644 index 0000000000..0a2e5f5e32 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.h @@ -0,0 +1,42 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +// NOTE: This element is marked as obsolete, but is still listed as required by the specification. +class HTMLMarqueeElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLMarqueeElementWrapper; + + HTMLMarqueeElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLMarqueeElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.idl new file mode 100644 index 0000000000..49ff70654d --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.idl @@ -0,0 +1,9 @@ +interface HTMLMarqueeElement : HTMLElement { + + [Reflect] attribute DOMString behaviour; + [Reflect=bgcolor] attribute DOMString bgColor; + [Reflect] attribute DOMString direction; + [Reflect] attribute DOMString height; + [Reflect] attribute DOMString width; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.cpp new file mode 100644 index 0000000000..15e4c01195 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLMediaElement.h> + +namespace Web::HTML { + +HTMLMediaElement::HTMLMediaElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLMediaElement::~HTMLMediaElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.h b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.h new file mode 100644 index 0000000000..53adde12e3 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLMediaElement : public HTMLElement { +public: + using WrapperType = Bindings::HTMLMediaElementWrapper; + + HTMLMediaElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLMediaElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.idl new file mode 100644 index 0000000000..dda7615580 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.idl @@ -0,0 +1,10 @@ +interface HTMLMediaElement : HTMLElement { + + [Reflect] attribute DOMString src; + + [Reflect] attribute boolean autoplay; + [Reflect] attribute boolean loop; + + [Reflect] attribute boolean controls; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.cpp new file mode 100644 index 0000000000..bcda7e5f56 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLMenuElement.h> + +namespace Web::HTML { + +HTMLMenuElement::HTMLMenuElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLMenuElement::~HTMLMenuElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.h b/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.h new file mode 100644 index 0000000000..8fe2acca90 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLMenuElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLMenuElementWrapper; + + HTMLMenuElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLMenuElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.idl new file mode 100644 index 0000000000..f9dc7ed7a0 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.idl @@ -0,0 +1,5 @@ +interface HTMLMenuElement : HTMLElement { + + [Reflect] attribute boolean compact; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.cpp new file mode 100644 index 0000000000..64c50a19e3 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLMetaElement.h> + +namespace Web::HTML { + +HTMLMetaElement::HTMLMetaElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLMetaElement::~HTMLMetaElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.h b/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.h new file mode 100644 index 0000000000..e48271c826 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLMetaElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLMetaElementWrapper; + + HTMLMetaElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLMetaElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.idl new file mode 100644 index 0000000000..de1868b65c --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.idl @@ -0,0 +1,9 @@ +interface HTMLMetaElement : HTMLElement { + + [Reflect] attribute DOMString name; + [Reflect] attribute DOMString content; + [Reflect=http-equiv] attribute DOMString httpEquiv; + + [Reflect] attribute DOMString scheme; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.cpp new file mode 100644 index 0000000000..07bfeee98f --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLMeterElement.h> + +namespace Web::HTML { + +HTMLMeterElement::HTMLMeterElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLMeterElement::~HTMLMeterElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.h b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.h new file mode 100644 index 0000000000..f6d983a6b0 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLMeterElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLMeterElementWrapper; + + HTMLMeterElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLMeterElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.idl new file mode 100644 index 0000000000..72f6c1d4fe --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.idl @@ -0,0 +1,5 @@ +interface HTMLMeterElement : HTMLElement { + + + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLModElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLModElement.cpp new file mode 100644 index 0000000000..5fcd7603b0 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLModElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLModElement.h> + +namespace Web::HTML { + +HTMLModElement::HTMLModElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLModElement::~HTMLModElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLModElement.h b/Userland/Libraries/LibWeb/HTML/HTMLModElement.h new file mode 100644 index 0000000000..f75c8bef9e --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLModElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLModElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLModElementWrapper; + + HTMLModElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLModElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLModElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLModElement.idl new file mode 100644 index 0000000000..8e3a489fa4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLModElement.idl @@ -0,0 +1,6 @@ +interface HTMLModElement : HTMLElement { + + [Reflect] attribute DOMString cite; + [Reflect=datetime] attribute DOMString dateTime; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOListElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.cpp new file mode 100644 index 0000000000..48ac66fd8d --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLOListElement.h> + +namespace Web::HTML { + +HTMLOListElement::HTMLOListElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLOListElement::~HTMLOListElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOListElement.h b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.h new file mode 100644 index 0000000000..8f5a849c75 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLOListElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLOListElementWrapper; + + HTMLOListElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLOListElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOListElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.idl new file mode 100644 index 0000000000..c81faf9c0e --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.idl @@ -0,0 +1,8 @@ +interface HTMLOListElement : HTMLElement { + + [Reflect] attribute boolean reversed; + [Reflect] attribute DOMString type; + + [Reflect] attribute boolean compact; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.cpp new file mode 100644 index 0000000000..a7514179d4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.cpp @@ -0,0 +1,77 @@ +/* + * 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 <LibGfx/Bitmap.h> +#include <LibGfx/ImageDecoder.h> +#include <LibWeb/CSS/StyleResolver.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/HTML/HTMLObjectElement.h> +#include <LibWeb/Layout/ImageBox.h> +#include <LibWeb/Loader/ResourceLoader.h> + +namespace Web::HTML { + +HTMLObjectElement::HTMLObjectElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ + m_image_loader.on_load = [this] { + m_should_show_fallback_content = false; + this->document().force_layout(); + }; + + m_image_loader.on_fail = [this] { + m_should_show_fallback_content = true; + this->document().force_layout(); + }; +} + +HTMLObjectElement::~HTMLObjectElement() +{ +} + +void HTMLObjectElement::parse_attribute(const FlyString& name, const String& value) +{ + HTMLElement::parse_attribute(name, value); + + if (name == HTML::AttributeNames::data) + m_image_loader.load(document().complete_url(value)); +} + +RefPtr<Layout::Node> HTMLObjectElement::create_layout_node() +{ + if (m_should_show_fallback_content) + return HTMLElement::create_layout_node(); + + auto style = document().style_resolver().resolve_style(*this); + if (style->display() == CSS::Display::None) + return nullptr; + if (m_image_loader.has_image()) + return adopt(*new Layout::ImageBox(document(), *this, move(style), m_image_loader)); + return nullptr; +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.h b/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.h new file mode 100644 index 0000000000..f07e122ff7 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.h @@ -0,0 +1,55 @@ +/* + * 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 <LibCore/Forward.h> +#include <LibGfx/Forward.h> +#include <LibWeb/HTML/HTMLElement.h> +#include <LibWeb/Loader/ImageLoader.h> + +namespace Web::HTML { + +class HTMLObjectElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLObjectElementWrapper; + + HTMLObjectElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLObjectElement() override; + + virtual void parse_attribute(const FlyString& name, const String& value) override; + + String data() const { return attribute(HTML::AttributeNames::data); } + String type() const { return attribute(HTML::AttributeNames::type); } + +private: + virtual RefPtr<Layout::Node> create_layout_node() override; + + ImageLoader m_image_loader; + bool m_should_show_fallback_content { false }; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.idl new file mode 100644 index 0000000000..3c5790bcc5 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.idl @@ -0,0 +1,19 @@ +interface HTMLObjectElement : HTMLElement { + + [Reflect] attribute DOMString data; + [Reflect] attribute DOMString type; + [Reflect] attribute DOMString name; + [Reflect=usemap] attribute DOMString useMap; + [Reflect] attribute DOMString width; + [Reflect] attribute DOMString height; + + [Reflect] attribute DOMString align; + [Reflect] attribute DOMString archive; + [Reflect] attribute DOMString code; + [Reflect] attribute boolean declare; + [Reflect] attribute DOMString standby; + [Reflect=codetype] attribute DOMString codeType; + + [LegacyNullToEmptyString, Reflect] attribute DOMString border; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.cpp new file mode 100644 index 0000000000..50c7d23b6f --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLOptGroupElement.h> + +namespace Web::HTML { + +HTMLOptGroupElement::HTMLOptGroupElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLOptGroupElement::~HTMLOptGroupElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.h b/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.h new file mode 100644 index 0000000000..77a90aebcb --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLOptGroupElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLOptGroupElementWrapper; + + HTMLOptGroupElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLOptGroupElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.idl new file mode 100644 index 0000000000..d442091b1a --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.idl @@ -0,0 +1,6 @@ +interface HTMLOptGroupElement : HTMLElement { + + [Reflect] attribute boolean disabled; + [Reflect] attribute DOMString label; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.cpp new file mode 100644 index 0000000000..c22fbb3668 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLOptionElement.h> + +namespace Web::HTML { + +HTMLOptionElement::HTMLOptionElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLOptionElement::~HTMLOptionElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.h b/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.h new file mode 100644 index 0000000000..e2bead49f6 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLOptionElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLOptionElementWrapper; + + HTMLOptionElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLOptionElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.idl new file mode 100644 index 0000000000..96dcaec548 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.idl @@ -0,0 +1,6 @@ +interface HTMLOptionElement : HTMLElement { + + [Reflect] attribute boolean disabled; + [Reflect=selected] attribute boolean defaultSelected; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.cpp new file mode 100644 index 0000000000..80f9d82dd5 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLOutputElement.h> + +namespace Web::HTML { + +HTMLOutputElement::HTMLOutputElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLOutputElement::~HTMLOutputElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.h new file mode 100644 index 0000000000..110225535f --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.h @@ -0,0 +1,47 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLOutputElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLOutputElementWrapper; + + HTMLOutputElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLOutputElement() override; + + const String& type() const + { + static String output = "output"; + return output; + } +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.idl new file mode 100644 index 0000000000..94c45e99f3 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.idl @@ -0,0 +1,5 @@ +interface HTMLOutputElement : HTMLElement { + + readonly attribute DOMString type; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.cpp new file mode 100644 index 0000000000..9e0fab9859 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLParagraphElement.h> + +namespace Web::HTML { + +HTMLParagraphElement::HTMLParagraphElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLParagraphElement::~HTMLParagraphElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.h b/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.h new file mode 100644 index 0000000000..660855edb9 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLParagraphElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLParagraphElementWrapper; + + HTMLParagraphElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLParagraphElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.idl new file mode 100644 index 0000000000..e1248da905 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.idl @@ -0,0 +1,5 @@ +interface HTMLParagraphElement : HTMLElement { + + [Reflect] attribute DOMString align; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLParamElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLParamElement.cpp new file mode 100644 index 0000000000..838ae13ef8 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLParamElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLParamElement.h> + +namespace Web::HTML { + +HTMLParamElement::HTMLParamElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLParamElement::~HTMLParamElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLParamElement.h b/Userland/Libraries/LibWeb/HTML/HTMLParamElement.h new file mode 100644 index 0000000000..642551cdd0 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLParamElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLParamElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLParamElementWrapper; + + HTMLParamElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLParamElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLParamElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLParamElement.idl new file mode 100644 index 0000000000..a848fc5364 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLParamElement.idl @@ -0,0 +1,9 @@ +interface HTMLParamElement : HTMLElement { + + [Reflect] attribute DOMString name; + [Reflect] attribute DOMString value; + + [Reflect] attribute DOMString type; + [Reflect=valuetype] attribute DOMString valueType; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.cpp new file mode 100644 index 0000000000..5aec65b51a --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLPictureElement.h> + +namespace Web::HTML { + +HTMLPictureElement::HTMLPictureElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLPictureElement::~HTMLPictureElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.h b/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.h new file mode 100644 index 0000000000..8d12df2b16 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLPictureElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLPictureElementWrapper; + + HTMLPictureElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLPictureElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.idl new file mode 100644 index 0000000000..9bd6cefe16 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.idl @@ -0,0 +1,5 @@ +interface HTMLPictureElement : HTMLElement { + + + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLPreElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLPreElement.cpp new file mode 100644 index 0000000000..69349d7238 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLPreElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLPreElement.h> + +namespace Web::HTML { + +HTMLPreElement::HTMLPreElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLPreElement::~HTMLPreElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLPreElement.h b/Userland/Libraries/LibWeb/HTML/HTMLPreElement.h new file mode 100644 index 0000000000..b87197f600 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLPreElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLPreElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLPreElementWrapper; + + HTMLPreElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLPreElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLPreElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLPreElement.idl new file mode 100644 index 0000000000..27a0404ba5 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLPreElement.idl @@ -0,0 +1,5 @@ +interface HTMLPreElement : HTMLElement { + + + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.cpp new file mode 100644 index 0000000000..cfad730312 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLProgressElement.h> + +namespace Web::HTML { + +HTMLProgressElement::HTMLProgressElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLProgressElement::~HTMLProgressElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.h b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.h new file mode 100644 index 0000000000..cb18e317f2 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLProgressElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLProgressElementWrapper; + + HTMLProgressElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLProgressElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.idl new file mode 100644 index 0000000000..1d00f9b3fc --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.idl @@ -0,0 +1,5 @@ +interface HTMLProgressElement : HTMLElement { + + + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.cpp new file mode 100644 index 0000000000..07bf149640 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLQuoteElement.h> + +namespace Web::HTML { + +HTMLQuoteElement::HTMLQuoteElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLQuoteElement::~HTMLQuoteElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.h b/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.h new file mode 100644 index 0000000000..98e254f213 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLQuoteElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLQuoteElementWrapper; + + HTMLQuoteElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLQuoteElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.idl new file mode 100644 index 0000000000..390074f746 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.idl @@ -0,0 +1,5 @@ +interface HTMLQuoteElement : HTMLElement { + + [Reflect] attribute DOMString cite; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.cpp new file mode 100644 index 0000000000..cc3031984c --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.cpp @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2018-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/StringBuilder.h> +#include <LibJS/Parser.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/DOM/Text.h> +#include <LibWeb/HTML/EventNames.h> +#include <LibWeb/HTML/HTMLScriptElement.h> +#include <LibWeb/Loader/ResourceLoader.h> + +namespace Web::HTML { + +HTMLScriptElement::HTMLScriptElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLScriptElement::~HTMLScriptElement() +{ +} + +void HTMLScriptElement::set_parser_document(Badge<HTMLDocumentParser>, DOM::Document& document) +{ + m_parser_document = document; +} + +void HTMLScriptElement::set_non_blocking(Badge<HTMLDocumentParser>, bool non_blocking) +{ + m_non_blocking = non_blocking; +} + +void HTMLScriptElement::execute_script() +{ + document().run_javascript(m_script_source); + + if (has_attribute(HTML::AttributeNames::src)) + dispatch_event(DOM::Event::create(EventNames::load)); +} + +void HTMLScriptElement::prepare_script(Badge<HTMLDocumentParser>) +{ + if (m_already_started) + return; + RefPtr<DOM::Document> parser_document = m_parser_document.ptr(); + m_parser_document = nullptr; + + if (parser_document && !has_attribute(HTML::AttributeNames::async)) { + m_non_blocking = true; + } + + auto source_text = child_text_content(); + if (!has_attribute(HTML::AttributeNames::src) && source_text.is_empty()) + return; + + if (!is_connected()) + return; + + // FIXME: Check the "type" and "language" attributes + + if (parser_document) { + m_parser_document = *parser_document; + m_non_blocking = false; + } + + m_already_started = true; + m_preparation_time_document = document(); + + if (parser_document && parser_document.ptr() != m_preparation_time_document.ptr()) { + return; + } + + // FIXME: Check if scripting is disabled, if so return + // FIXME: Check the "nomodule" content attribute + // FIXME: Check CSP + // FIXME: Check "event" and "for" attributes + // FIXME: Check "charset" attribute + // FIXME: Check CORS + // FIXME: Module script credentials mode + // FIXME: Cryptographic nonce + // FIXME: Check "integrity" attribute + // FIXME: Check "referrerpolicy" attribute + + m_parser_inserted = !!m_parser_document; + + // FIXME: Check fetch options + + if (has_attribute(HTML::AttributeNames::src)) { + auto src = attribute(HTML::AttributeNames::src); + if (src.is_empty()) { + // FIXME: Fire an "error" event at the element and return + ASSERT_NOT_REACHED(); + } + m_from_an_external_file = true; + + auto url = document().complete_url(src); + if (!url.is_valid()) { + // FIXME: Fire an "error" event at the element and return + ASSERT_NOT_REACHED(); + } + + // FIXME: Check classic vs. module script type + + // FIXME: This load should be made asynchronous and the parser should spin an event loop etc. + ResourceLoader::the().load_sync( + url, + [this, url](auto data, auto&) { + if (data.is_null()) { + dbgln("HTMLScriptElement: Failed to load {}", url); + return; + } + m_script_source = String::copy(data); + script_became_ready(); + }, + [this](auto&) { + m_failed_to_load = true; + }); + } else { + // FIXME: Check classic vs. module script type + m_script_source = source_text; + script_became_ready(); + } + + // FIXME: Check classic vs. module + if (has_attribute(HTML::AttributeNames::src) && has_attribute(HTML::AttributeNames::defer) && m_parser_inserted && !has_attribute(HTML::AttributeNames::async)) { + document().add_script_to_execute_when_parsing_has_finished({}, *this); + } + + else if (has_attribute(HTML::AttributeNames::src) && m_parser_inserted && !has_attribute(HTML::AttributeNames::async)) { + + document().set_pending_parsing_blocking_script({}, this); + when_the_script_is_ready([this] { + m_ready_to_be_parser_executed = true; + }); + } + + else if (has_attribute(HTML::AttributeNames::src) && !has_attribute(HTML::AttributeNames::async) && !m_non_blocking) { + ASSERT_NOT_REACHED(); + } + + else if (has_attribute(HTML::AttributeNames::src)) { + m_preparation_time_document->add_script_to_execute_as_soon_as_possible({}, *this); + } + + else { + // Immediately execute the script block, even if other scripts are already executing. + execute_script(); + } +} + +void HTMLScriptElement::script_became_ready() +{ + m_script_ready = true; + if (!m_script_ready_callback) + return; + m_script_ready_callback(); + m_script_ready_callback = nullptr; +} + +void HTMLScriptElement::when_the_script_is_ready(Function<void()> callback) +{ + if (m_script_ready) { + callback(); + return; + } + m_script_ready_callback = move(callback); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.h b/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.h new file mode 100644 index 0000000000..b15afcdd7a --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018-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 <AK/Function.h> +#include <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLScriptElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLScriptElementWrapper; + + HTMLScriptElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLScriptElement() override; + + bool is_non_blocking() const { return m_non_blocking; } + bool is_ready_to_be_parser_executed() const { return m_ready_to_be_parser_executed; } + bool failed_to_load() const { return m_failed_to_load; } + + void set_parser_document(Badge<HTMLDocumentParser>, DOM::Document&); + void set_non_blocking(Badge<HTMLDocumentParser>, bool); + void set_already_started(Badge<HTMLDocumentParser>, bool b) { m_already_started = b; } + void prepare_script(Badge<HTMLDocumentParser>); + void execute_script(); + +private: + void script_became_ready(); + void when_the_script_is_ready(Function<void()>); + + WeakPtr<DOM::Document> m_parser_document; + WeakPtr<DOM::Document> m_preparation_time_document; + bool m_non_blocking { false }; + bool m_already_started { false }; + bool m_parser_inserted { false }; + bool m_from_an_external_file { false }; + bool m_script_ready { false }; + bool m_ready_to_be_parser_executed { false }; + bool m_failed_to_load { false }; + + Function<void()> m_script_ready_callback; + + String m_script_source; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.idl new file mode 100644 index 0000000000..370dadd29a --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.idl @@ -0,0 +1,13 @@ +interface HTMLScriptElement : HTMLElement { + + [Reflect] attribute DOMString src; + [Reflect] attribute DOMString type; + [Reflect=nomodule] attribute boolean noModule; + [Reflect] attribute boolean defer; + [Reflect] attribute DOMString integrity; + + [Reflect] attribute DOMString charset; + [Reflect] attribute DOMString event; + [Reflect=for] attribute DOMString htmlFor; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.cpp new file mode 100644 index 0000000000..9c4b486b05 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLSelectElement.h> + +namespace Web::HTML { + +HTMLSelectElement::HTMLSelectElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLSelectElement::~HTMLSelectElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.h b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.h new file mode 100644 index 0000000000..49d66970b9 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLSelectElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLSelectElementWrapper; + + HTMLSelectElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLSelectElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl new file mode 100644 index 0000000000..808db0b45f --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl @@ -0,0 +1,7 @@ +interface HTMLSelectElement : HTMLElement { + + [Reflect] attribute boolean disabled; + [Reflect] attribute boolean multiple; + [Reflect] attribute boolean required; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.cpp new file mode 100644 index 0000000000..cb32ab7772 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLSlotElement.h> + +namespace Web::HTML { + +HTMLSlotElement::HTMLSlotElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLSlotElement::~HTMLSlotElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.h b/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.h new file mode 100644 index 0000000000..96c42ff6f2 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLSlotElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLSlotElementWrapper; + + HTMLSlotElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLSlotElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.idl new file mode 100644 index 0000000000..04dc7a7111 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.idl @@ -0,0 +1,5 @@ +interface HTMLSlotElement : HTMLElement { + + [Reflect] attribute DOMString name; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.cpp new file mode 100644 index 0000000000..3dcf89815e --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLSourceElement.h> + +namespace Web::HTML { + +HTMLSourceElement::HTMLSourceElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLSourceElement::~HTMLSourceElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.h b/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.h new file mode 100644 index 0000000000..f126968ddd --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLSourceElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLSourceElementWrapper; + + HTMLSourceElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLSourceElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.idl new file mode 100644 index 0000000000..5496d55a1a --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.idl @@ -0,0 +1,9 @@ +interface HTMLSourceElement : HTMLElement { + + [Reflect] attribute DOMString src; + [Reflect] attribute DOMString type; + [Reflect] attribute DOMString srcset; + [Reflect] attribute DOMString sizes; + [Reflect] attribute DOMString media; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.cpp new file mode 100644 index 0000000000..568d24f891 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLSpanElement.h> + +namespace Web::HTML { + +HTMLSpanElement::HTMLSpanElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLSpanElement::~HTMLSpanElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.h b/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.h new file mode 100644 index 0000000000..1906eb2a56 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLSpanElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLSpanElementWrapper; + + HTMLSpanElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLSpanElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.idl new file mode 100644 index 0000000000..a87dda0eb7 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.idl @@ -0,0 +1,5 @@ +interface HTMLSpanElement : HTMLElement { + + + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.cpp new file mode 100644 index 0000000000..44fa6daea0 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018-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/StringBuilder.h> +#include <LibWeb/CSS/Parser/CSSParser.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Text.h> +#include <LibWeb/HTML/HTMLStyleElement.h> + +namespace Web::HTML { + +HTMLStyleElement::HTMLStyleElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLStyleElement::~HTMLStyleElement() +{ +} + +void HTMLStyleElement::children_changed() +{ + StringBuilder builder; + for_each_child([&](auto& child) { + if (is<DOM::Text>(child)) + builder.append(downcast<DOM::Text>(child).text_content()); + }); + m_stylesheet = parse_css(CSS::ParsingContext(document()), builder.to_string()); + if (m_stylesheet) + document().style_sheets().add_sheet(*m_stylesheet); + else + document().style_sheets().add_sheet(CSS::StyleSheet::create({})); + HTMLElement::children_changed(); +} + +void HTMLStyleElement::removed_from(Node& old_parent) +{ + if (m_stylesheet) { + // FIXME: Remove the sheet from the document + } + return HTMLElement::removed_from(old_parent); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.h b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.h new file mode 100644 index 0000000000..9e3b615c0c --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-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/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLStyleElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLStyleElementWrapper; + + HTMLStyleElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLStyleElement() override; + + virtual void children_changed() override; + virtual void removed_from(Node&) override; + +private: + RefPtr<CSS::StyleSheet> m_stylesheet; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.idl new file mode 100644 index 0000000000..a5ab2934f3 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.idl @@ -0,0 +1,7 @@ +interface HTMLStyleElement : HTMLElement { + + [Reflect] attribute DOMString media; + + [Reflect] attribute DOMString type; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.cpp new file mode 100644 index 0000000000..a9c3e62a98 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLTableCaptionElement.h> + +namespace Web::HTML { + +HTMLTableCaptionElement::HTMLTableCaptionElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLTableCaptionElement::~HTMLTableCaptionElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.h new file mode 100644 index 0000000000..691b49ab3c --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLTableCaptionElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLTableCaptionElementWrapper; + + HTMLTableCaptionElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLTableCaptionElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.idl new file mode 100644 index 0000000000..97d9419ccf --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.idl @@ -0,0 +1,5 @@ +interface HTMLTableCaptionElement : HTMLElement { + + [Reflect] attribute DOMString align; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.cpp new file mode 100644 index 0000000000..5dd60f2cbb --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.cpp @@ -0,0 +1,65 @@ +/* + * 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 <LibWeb/CSS/Parser/CSSParser.h> +#include <LibWeb/HTML/HTMLTableCellElement.h> + +namespace Web::HTML { + +HTMLTableCellElement::HTMLTableCellElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLTableCellElement::~HTMLTableCellElement() +{ +} + +void HTMLTableCellElement::apply_presentational_hints(CSS::StyleProperties& style) const +{ + for_each_attribute([&](auto& name, auto& value) { + if (name == HTML::AttributeNames::bgcolor) { + auto color = Color::from_string(value); + if (color.has_value()) + style.set_property(CSS::PropertyID::BackgroundColor, CSS::ColorStyleValue::create(color.value())); + return; + } + if (name == HTML::AttributeNames::align) { + if (value.equals_ignoring_case("center") || value.equals_ignoring_case("middle")) + style.set_property(CSS::PropertyID::TextAlign, StringView("-libweb-center")); + else + style.set_property(CSS::PropertyID::TextAlign, value.view()); + return; + } + if (name == HTML::AttributeNames::width) { + if (auto parsed_value = parse_html_length(document(), value)) + style.set_property(CSS::PropertyID::Width, parsed_value.release_nonnull()); + return; + } + }); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.h new file mode 100644 index 0000000000..7a2b9e4f09 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.h @@ -0,0 +1,44 @@ +/* + * 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/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLTableCellElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLTableCellElementWrapper; + + HTMLTableCellElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLTableCellElement() override; + +private: + virtual void apply_presentational_hints(CSS::StyleProperties&) const override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.idl new file mode 100644 index 0000000000..bcf8f1a04a --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.idl @@ -0,0 +1,18 @@ +interface HTMLTableCellElement : HTMLElement { + + [Reflect] attribute DOMString headers; + [Reflect] attribute DOMString abbr; + + [Reflect] attribute DOMString align; + [Reflect] attribute DOMString axis; + [Reflect] attribute DOMString height; + [Reflect] attribute DOMString width; + + [Reflect=char] attribute DOMString ch; + [Reflect=charoff] attribute DOMString chOff; + [Reflect=nowrap] attribute boolean noWrap; + [Reflect=valign] attribute DOMString vAlign; + + [LegacyNullToEmptyString, Reflect=bgcolor] attribute DOMString bgColor; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.cpp new file mode 100644 index 0000000000..17a3e494c8 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLTableColElement.h> + +namespace Web::HTML { + +HTMLTableColElement::HTMLTableColElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLTableColElement::~HTMLTableColElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.h new file mode 100644 index 0000000000..c6b8e706ff --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLTableColElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLTableColElementWrapper; + + HTMLTableColElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLTableColElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.idl new file mode 100644 index 0000000000..d76f0da76b --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.idl @@ -0,0 +1,9 @@ +interface HTMLTableColElement : HTMLElement { + + [Reflect] attribute DOMString align; + [Reflect=char] attribute DOMString ch; + [Reflect=charoff] attribute DOMString chOff; + [Reflect=valign] attribute DOMString vAlign; + [Reflect] attribute DOMString width; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTableElement.cpp new file mode 100644 index 0000000000..d31641af42 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableElement.cpp @@ -0,0 +1,63 @@ +/* + * 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 <LibWeb/CSS/Parser/CSSParser.h> +#include <LibWeb/HTML/HTMLTableElement.h> + +namespace Web::HTML { + +HTMLTableElement::HTMLTableElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLTableElement::~HTMLTableElement() +{ +} + +void HTMLTableElement::apply_presentational_hints(CSS::StyleProperties& style) const +{ + for_each_attribute([&](auto& name, auto& value) { + if (name == HTML::AttributeNames::width) { + if (auto parsed_value = parse_html_length(document(), value)) + style.set_property(CSS::PropertyID::Width, parsed_value.release_nonnull()); + return; + } + if (name == HTML::AttributeNames::height) { + if (auto parsed_value = parse_html_length(document(), value)) + style.set_property(CSS::PropertyID::Height, parsed_value.release_nonnull()); + return; + } + if (name == HTML::AttributeNames::bgcolor) { + auto color = Color::from_string(value); + if (color.has_value()) + style.set_property(CSS::PropertyID::BackgroundColor, CSS::ColorStyleValue::create(color.value())); + return; + } + }); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTableElement.h new file mode 100644 index 0000000000..39d65833c6 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableElement.h @@ -0,0 +1,44 @@ +/* + * 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/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLTableElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLTableElementWrapper; + + HTMLTableElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLTableElement() override; + +private: + virtual void apply_presentational_hints(CSS::StyleProperties&) const override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTableElement.idl new file mode 100644 index 0000000000..0990efa4a1 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableElement.idl @@ -0,0 +1,14 @@ +interface HTMLTableElement : HTMLElement { + + [Reflect] attribute DOMString align; + [Reflect] attribute DOMString border; + [Reflect] attribute DOMString frame; + [Reflect] attribute DOMString rules; + [Reflect] attribute DOMString summary; + [Reflect] attribute DOMString width; + + [LegacyNullToEmptyString, Reflect=bgcolor] attribute DOMString bgColor; + [LegacyNullToEmptyString, Reflect=cellpadding] attribute DOMString cellPadding; + [LegacyNullToEmptyString, Reflect=cellspacing] attribute DOMString cellSpacing; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.cpp new file mode 100644 index 0000000000..28f0d6d910 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLTableRowElement.h> + +namespace Web::HTML { + +HTMLTableRowElement::HTMLTableRowElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLTableRowElement::~HTMLTableRowElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.h new file mode 100644 index 0000000000..dc64d13322 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.h @@ -0,0 +1,41 @@ +/* + * 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/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLTableRowElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLTableRowElementWrapper; + + HTMLTableRowElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLTableRowElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.idl new file mode 100644 index 0000000000..1b999948f4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.idl @@ -0,0 +1,10 @@ +interface HTMLTableRowElement : HTMLElement { + + [Reflect] attribute DOMString align; + [Reflect=char] attribute DOMString ch; + [Reflect=charoff] attribute DOMString chOff; + [Reflect=valign] attribute DOMString vAlign; + + [LegacyNullToEmptyString, Reflect=bgcolor] attribute DOMString bgColor; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.cpp new file mode 100644 index 0000000000..2ce4394b94 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLTableSectionElement.h> + +namespace Web::HTML { + +HTMLTableSectionElement::HTMLTableSectionElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLTableSectionElement::~HTMLTableSectionElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.h new file mode 100644 index 0000000000..dc58ea8a1b --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLTableSectionElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLTableSectionElementWrapper; + + HTMLTableSectionElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLTableSectionElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.idl new file mode 100644 index 0000000000..4201411b8c --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.idl @@ -0,0 +1,8 @@ +interface HTMLTableSectionElement : HTMLElement { + + [Reflect] attribute DOMString align; + [Reflect=char] attribute DOMString ch; + [Reflect=charoff] attribute DOMString chOff; + [Reflect=valign] attribute DOMString vAlign; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.cpp new file mode 100644 index 0000000000..8a0209337b --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.cpp @@ -0,0 +1,61 @@ +/* + * 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 <LibWeb/DOM/Document.h> +#include <LibWeb/HTML/HTMLTemplateElement.h> + +namespace Web::HTML { + +HTMLTemplateElement::HTMLTemplateElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ + m_content = adopt(*new DOM::DocumentFragment(appropriate_template_contents_owner_document(document))); + m_content->set_host(*this); +} + +HTMLTemplateElement::~HTMLTemplateElement() +{ +} + +DOM::Document& HTMLTemplateElement::appropriate_template_contents_owner_document(DOM::Document& document) +{ + if (!document.created_for_appropriate_template_contents()) { + if (!document.associated_inert_template_document()) { + auto new_document = DOM::Document::create(); + new_document->set_created_for_appropriate_template_contents(true); + + // FIXME: If doc is an HTML document, mark new doc as an HTML document also. + + document.set_associated_inert_template_document(new_document); + } + + return *document.associated_inert_template_document(); + } + + return document; +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.h new file mode 100644 index 0000000000..8e26320684 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.h @@ -0,0 +1,50 @@ +/* + * 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 <LibWeb/DOM/DocumentFragment.h> +#include <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLTemplateElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLTemplateElementWrapper; + + HTMLTemplateElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLTemplateElement() override; + + NonnullRefPtr<DOM::DocumentFragment> content() { return *m_content; } + const NonnullRefPtr<DOM::DocumentFragment> content() const { return *m_content; } + +private: + DOM::Document& appropriate_template_contents_owner_document(DOM::Document&); + + RefPtr<DOM::DocumentFragment> m_content; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.idl new file mode 100644 index 0000000000..efbbc57e02 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.idl @@ -0,0 +1,5 @@ +interface HTMLTemplateElement : HTMLElement { + + readonly attribute DocumentFragment content; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp new file mode 100644 index 0000000000..29433042c4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLTextAreaElement.h> + +namespace Web::HTML { + +HTMLTextAreaElement::HTMLTextAreaElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLTextAreaElement::~HTMLTextAreaElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h new file mode 100644 index 0000000000..e09b95541a --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h @@ -0,0 +1,47 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLTextAreaElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLTextAreaElementWrapper; + + HTMLTextAreaElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLTextAreaElement() override; + + const String& type() const + { + static String textarea = "textarea"; + return textarea; + } +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.idl new file mode 100644 index 0000000000..878cc67db0 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.idl @@ -0,0 +1,12 @@ +interface HTMLTextAreaElement : HTMLElement { + + [Reflect] attribute DOMString placeholder; + [Reflect] attribute DOMString name; + [Reflect] attribute DOMString wrap; + readonly attribute DOMString type; + + [Reflect] attribute boolean disabled; + [Reflect=readonly] attribute boolean readOnly; + [Reflect] attribute boolean required; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.cpp new file mode 100644 index 0000000000..613f8aadbe --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLTimeElement.h> + +namespace Web::HTML { + +HTMLTimeElement::HTMLTimeElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLTimeElement::~HTMLTimeElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.h new file mode 100644 index 0000000000..aacef209a9 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLTimeElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLTimeElementWrapper; + + HTMLTimeElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLTimeElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.idl new file mode 100644 index 0000000000..f82d1e1aea --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.idl @@ -0,0 +1,5 @@ +interface HTMLTimeElement : HTMLElement { + + [Reflect=datetime] attribute DOMString dateTime; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.cpp new file mode 100644 index 0000000000..a1404dfe9e --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-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 <LibWeb/DOM/Document.h> +#include <LibWeb/HTML/HTMLTitleElement.h> +#include <LibWeb/Page/Page.h> + +namespace Web::HTML { + +HTMLTitleElement::HTMLTitleElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLTitleElement::~HTMLTitleElement() +{ +} + +void HTMLTitleElement::children_changed() +{ + HTMLElement::children_changed(); + if (auto* page = document().page()) + page->client().page_did_change_title(document().title()); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.h new file mode 100644 index 0000000000..42c28f467f --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-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/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLTitleElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLTitleElementWrapper; + + HTMLTitleElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLTitleElement() override; + +private: + virtual void children_changed() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.idl new file mode 100644 index 0000000000..6cfbc9ef29 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.idl @@ -0,0 +1,5 @@ +interface HTMLTitleElement : HTMLElement { + + + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.cpp new file mode 100644 index 0000000000..2f0cee7f28 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLTrackElement.h> + +namespace Web::HTML { + +HTMLTrackElement::HTMLTrackElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLTrackElement::~HTMLTrackElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.h new file mode 100644 index 0000000000..6a0c78c176 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLTrackElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLTrackElementWrapper; + + HTMLTrackElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLTrackElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.idl new file mode 100644 index 0000000000..d58d662cb0 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.idl @@ -0,0 +1,8 @@ +interface HTMLTrackElement : HTMLElement { + + [Reflect] attribute DOMString src; + [Reflect] attribute DOMString srclang; + [Reflect] attribute DOMString label; + [Reflect] attribute boolean default; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLUListElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLUListElement.cpp new file mode 100644 index 0000000000..d9cbcb9cd7 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLUListElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLUListElement.h> + +namespace Web::HTML { + +HTMLUListElement::HTMLUListElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLUListElement::~HTMLUListElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLUListElement.h b/Userland/Libraries/LibWeb/HTML/HTMLUListElement.h new file mode 100644 index 0000000000..c4efa7949a --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLUListElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLUListElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLUListElementWrapper; + + HTMLUListElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLUListElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLUListElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLUListElement.idl new file mode 100644 index 0000000000..6490a6d5c8 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLUListElement.idl @@ -0,0 +1,6 @@ +interface HTMLUListElement : HTMLElement { + + [Reflect] attribute boolean compact; + [Reflect] attribute DOMString type; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.cpp new file mode 100644 index 0000000000..3ef559692d --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLUnknownElement.h> + +namespace Web::HTML { + +HTMLUnknownElement::HTMLUnknownElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLElement(document, qualified_name) +{ +} + +HTMLUnknownElement::~HTMLUnknownElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.h b/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.h new file mode 100644 index 0000000000..896df0d064 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLElement.h> + +namespace Web::HTML { + +class HTMLUnknownElement final : public HTMLElement { +public: + using WrapperType = Bindings::HTMLUnknownElementWrapper; + + HTMLUnknownElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLUnknownElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.idl new file mode 100644 index 0000000000..7a6672d4f4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.idl @@ -0,0 +1,5 @@ +interface HTMLUnknownElement : HTMLElement { + + + +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.cpp new file mode 100644 index 0000000000..cbd97199cf --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.cpp @@ -0,0 +1,40 @@ +/* + * 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 <LibWeb/HTML/HTMLVideoElement.h> + +namespace Web::HTML { + +HTMLVideoElement::HTMLVideoElement(DOM::Document& document, const QualifiedName& qualified_name) + : HTMLMediaElement(document, qualified_name) +{ +} + +HTMLVideoElement::~HTMLVideoElement() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.h b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.h new file mode 100644 index 0000000000..9349f0feb6 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.h @@ -0,0 +1,41 @@ +/* + * 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 <LibWeb/HTML/HTMLMediaElement.h> + +namespace Web::HTML { + +class HTMLVideoElement final : public HTMLMediaElement { +public: + using WrapperType = Bindings::HTMLVideoElementWrapper; + + HTMLVideoElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~HTMLVideoElement() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.idl new file mode 100644 index 0000000000..6fb6ba3a50 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.idl @@ -0,0 +1,6 @@ +interface HTMLVideoElement : HTMLMediaElement { + + [Reflect] attribute DOMString poster; + [Reflect=playsinline] attribute boolean playsInline; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/ImageData.cpp b/Userland/Libraries/LibWeb/HTML/ImageData.cpp new file mode 100644 index 0000000000..085cc88728 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/ImageData.cpp @@ -0,0 +1,85 @@ +/* + * 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 <LibGfx/Bitmap.h> +#include <LibJS/Runtime/Uint8ClampedArray.h> +#include <LibWeb/HTML/ImageData.h> + +namespace Web::HTML { + +RefPtr<ImageData> ImageData::create_with_size(JS::GlobalObject& global_object, int width, int height) +{ + if (width <= 0 || height <= 0) + return nullptr; + + if (width > 16384 || height > 16384) + return nullptr; + + dbgln("Creating ImageData with {}x{}", width, height); + + auto* data = JS::Uint8ClampedArray::create(global_object, width * height * 4); + if (!data) + return nullptr; + + auto data_handle = JS::make_handle(data); + + auto bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA32, Gfx::IntSize(width, height), width * sizeof(u32), (u32*)data->data()); + if (!bitmap) + return nullptr; + return adopt(*new ImageData(bitmap.release_nonnull(), move(data_handle))); +} + +ImageData::ImageData(NonnullRefPtr<Gfx::Bitmap> bitmap, JS::Handle<JS::Uint8ClampedArray> data) + : m_bitmap(move(bitmap)) + , m_data(move(data)) +{ +} + +ImageData::~ImageData() +{ +} + +unsigned ImageData::width() const +{ + return m_bitmap->width(); +} + +unsigned ImageData::height() const +{ + return m_bitmap->height(); +} + +JS::Uint8ClampedArray* ImageData::data() +{ + return m_data.cell(); +} + +const JS::Uint8ClampedArray* ImageData::data() const +{ + return m_data.cell(); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/ImageData.h b/Userland/Libraries/LibWeb/HTML/ImageData.h new file mode 100644 index 0000000000..810400d187 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/ImageData.h @@ -0,0 +1,61 @@ +/* + * 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 <LibGfx/Forward.h> +#include <LibJS/Heap/Handle.h> +#include <LibWeb/Bindings/Wrappable.h> + +namespace Web::HTML { + +class ImageData + : public RefCounted<ImageData> + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::ImageDataWrapper; + + static RefPtr<ImageData> create_with_size(JS::GlobalObject&, int width, int height); + + ~ImageData(); + + unsigned width() const; + unsigned height() const; + + Gfx::Bitmap& bitmap() { return m_bitmap; } + const Gfx::Bitmap& bitmap() const { return m_bitmap; } + + JS::Uint8ClampedArray* data(); + const JS::Uint8ClampedArray* data() const; + +private: + explicit ImageData(NonnullRefPtr<Gfx::Bitmap>, JS::Handle<JS::Uint8ClampedArray>); + + NonnullRefPtr<Gfx::Bitmap> m_bitmap; + JS::Handle<JS::Uint8ClampedArray> m_data; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/ImageData.idl b/Userland/Libraries/LibWeb/HTML/ImageData.idl new file mode 100644 index 0000000000..29b683b547 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/ImageData.idl @@ -0,0 +1,7 @@ +interface ImageData { + + readonly attribute unsigned long width; + readonly attribute unsigned long height; + readonly attribute Uint8ClampedArray data; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/Parser/Entities.cpp b/Userland/Libraries/LibWeb/HTML/Parser/Entities.cpp new file mode 100644 index 0000000000..edda468b0a --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Parser/Entities.cpp @@ -0,0 +1,2302 @@ +/* + * 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/LogStream.h> +#include <AK/StringView.h> +#include <LibWeb/HTML/Parser/Entities.h> + +namespace Web { +namespace HTML { + +Optional<EntityMatch> code_points_from_entity(const StringView& entity) +{ + constexpr struct { + StringView entity; + u32 code_point; + } single_code_point_entities[] = { + { "AElig;", 0x000C6 }, + { "AElig", 0x000C6 }, + { "AMP;", 0x00026 }, + { "AMP", 0x00026 }, + { "Aacute;", 0x000C1 }, + { "Aacute", 0x000C1 }, + { "Abreve;", 0x00102 }, + { "Acirc;", 0x000C2 }, + { "Acirc", 0x000C2 }, + { "Acy;", 0x00410 }, + { "Afr;", 0x1D504 }, + { "Agrave;", 0x000C0 }, + { "Agrave", 0x000C0 }, + { "Alpha;", 0x00391 }, + { "Amacr;", 0x00100 }, + { "And;", 0x02A53 }, + { "Aogon;", 0x00104 }, + { "Aopf;", 0x1D538 }, + { "ApplyFunction;", 0x02061 }, + { "Aring;", 0x000C5 }, + { "Aring", 0x000C5 }, + { "Ascr;", 0x1D49C }, + { "Assign;", 0x02254 }, + { "Atilde;", 0x000C3 }, + { "Atilde", 0x000C3 }, + { "Auml;", 0x000C4 }, + { "Auml", 0x000C4 }, + { "Backslash;", 0x02216 }, + { "Barv;", 0x02AE7 }, + { "Barwed;", 0x02306 }, + { "Bcy;", 0x00411 }, + { "Because;", 0x02235 }, + { "Bernoullis;", 0x0212C }, + { "Beta;", 0x00392 }, + { "Bfr;", 0x1D505 }, + { "Bopf;", 0x1D539 }, + { "Breve;", 0x002D8 }, + { "Bscr;", 0x0212C }, + { "Bumpeq;", 0x0224E }, + { "CHcy;", 0x00427 }, + { "COPY;", 0x000A9 }, + { "COPY", 0x000A9 }, + { "Cacute;", 0x00106 }, + { "Cap;", 0x022D2 }, + { "CapitalDifferentialD;", 0x02145 }, + { "Cayleys;", 0x0212D }, + { "Ccaron;", 0x0010C }, + { "Ccedil;", 0x000C7 }, + { "Ccedil", 0x000C7 }, + { "Ccirc;", 0x00108 }, + { "Cconint;", 0x02230 }, + { "Cdot;", 0x0010A }, + { "Cedilla;", 0x000B8 }, + { "CenterDot;", 0x000B7 }, + { "Cfr;", 0x0212D }, + { "Chi;", 0x003A7 }, + { "CircleDot;", 0x02299 }, + { "CircleMinus;", 0x02296 }, + { "CirclePlus;", 0x02295 }, + { "CircleTimes;", 0x02297 }, + { "ClockwiseContourIntegral;", 0x02232 }, + { "CloseCurlyDoubleQuote;", 0x0201D }, + { "CloseCurlyQuote;", 0x02019 }, + { "Colon;", 0x02237 }, + { "Colone;", 0x02A74 }, + { "Congruent;", 0x02261 }, + { "Conint;", 0x0222F }, + { "ContourIntegral;", 0x0222E }, + { "Copf;", 0x02102 }, + { "Coproduct;", 0x02210 }, + { "CounterClockwiseContourIntegral;", 0x02233 }, + { "Cross;", 0x02A2F }, + { "Cscr;", 0x1D49E }, + { "Cup;", 0x022D3 }, + { "CupCap;", 0x0224D }, + { "DD;", 0x02145 }, + { "DDotrahd;", 0x02911 }, + { "DJcy;", 0x00402 }, + { "DScy;", 0x00405 }, + { "DZcy;", 0x0040F }, + { "Dagger;", 0x02021 }, + { "Darr;", 0x021A1 }, + { "Dashv;", 0x02AE4 }, + { "Dcaron;", 0x0010E }, + { "Dcy;", 0x00414 }, + { "Del;", 0x02207 }, + { "Delta;", 0x00394 }, + { "Dfr;", 0x1D507 }, + { "DiacriticalAcute;", 0x000B4 }, + { "DiacriticalDot;", 0x002D9 }, + { "DiacriticalDoubleAcute;", 0x002DD }, + { "DiacriticalGrave;", 0x00060 }, + { "DiacriticalTilde;", 0x002DC }, + { "Diamond;", 0x022C4 }, + { "DifferentialD;", 0x02146 }, + { "Dopf;", 0x1D53B }, + { "Dot;", 0x000A8 }, + { "DotDot;", 0x020DC }, + { "DotEqual;", 0x02250 }, + { "DoubleContourIntegral;", 0x0222F }, + { "DoubleDot;", 0x000A8 }, + { "DoubleDownArrow;", 0x021D3 }, + { "DoubleLeftArrow;", 0x021D0 }, + { "DoubleLeftRightArrow;", 0x021D4 }, + { "DoubleLeftTee;", 0x02AE4 }, + { "DoubleLongLeftArrow;", 0x027F8 }, + { "DoubleLongLeftRightArrow;", 0x027FA }, + { "DoubleLongRightArrow;", 0x027F9 }, + { "DoubleRightArrow;", 0x021D2 }, + { "DoubleRightTee;", 0x022A8 }, + { "DoubleUpArrow;", 0x021D1 }, + { "DoubleUpDownArrow;", 0x021D5 }, + { "DoubleVerticalBar;", 0x02225 }, + { "DownArrow;", 0x02193 }, + { "DownArrowBar;", 0x02913 }, + { "DownArrowUpArrow;", 0x021F5 }, + { "DownBreve;", 0x00311 }, + { "DownLeftRightVector;", 0x02950 }, + { "DownLeftTeeVector;", 0x0295E }, + { "DownLeftVector;", 0x021BD }, + { "DownLeftVectorBar;", 0x02956 }, + { "DownRightTeeVector;", 0x0295F }, + { "DownRightVector;", 0x021C1 }, + { "DownRightVectorBar;", 0x02957 }, + { "DownTee;", 0x022A4 }, + { "DownTeeArrow;", 0x021A7 }, + { "Downarrow;", 0x021D3 }, + { "Dscr;", 0x1D49F }, + { "Dstrok;", 0x00110 }, + { "ENG;", 0x0014A }, + { "ETH;", 0x000D0 }, + { "ETH", 0x000D0 }, + { "Eacute;", 0x000C9 }, + { "Eacute", 0x000C9 }, + { "Ecaron;", 0x0011A }, + { "Ecirc;", 0x000CA }, + { "Ecirc", 0x000CA }, + { "Ecy;", 0x0042D }, + { "Edot;", 0x00116 }, + { "Efr;", 0x1D508 }, + { "Egrave;", 0x000C8 }, + { "Egrave", 0x000C8 }, + { "Element;", 0x02208 }, + { "Emacr;", 0x00112 }, + { "EmptySmallSquare;", 0x025FB }, + { "EmptyVerySmallSquare;", 0x025AB }, + { "Eogon;", 0x00118 }, + { "Eopf;", 0x1D53C }, + { "Epsilon;", 0x00395 }, + { "Equal;", 0x02A75 }, + { "EqualTilde;", 0x02242 }, + { "Equilibrium;", 0x021CC }, + { "Escr;", 0x02130 }, + { "Esim;", 0x02A73 }, + { "Eta;", 0x00397 }, + { "Euml;", 0x000CB }, + { "Euml", 0x000CB }, + { "Exists;", 0x02203 }, + { "ExponentialE;", 0x02147 }, + { "Fcy;", 0x00424 }, + { "Ffr;", 0x1D509 }, + { "FilledSmallSquare;", 0x025FC }, + { "FilledVerySmallSquare;", 0x025AA }, + { "Fopf;", 0x1D53D }, + { "ForAll;", 0x02200 }, + { "Fouriertrf;", 0x02131 }, + { "Fscr;", 0x02131 }, + { "GJcy;", 0x00403 }, + { "GT;", 0x0003E }, + { "GT", 0x0003E }, + { "Gamma;", 0x00393 }, + { "Gammad;", 0x003DC }, + { "Gbreve;", 0x0011E }, + { "Gcedil;", 0x00122 }, + { "Gcirc;", 0x0011C }, + { "Gcy;", 0x00413 }, + { "Gdot;", 0x00120 }, + { "Gfr;", 0x1D50A }, + { "Gg;", 0x022D9 }, + { "Gopf;", 0x1D53E }, + { "GreaterEqual;", 0x02265 }, + { "GreaterEqualLess;", 0x022DB }, + { "GreaterFullEqual;", 0x02267 }, + { "GreaterGreater;", 0x02AA2 }, + { "GreaterLess;", 0x02277 }, + { "GreaterSlantEqual;", 0x02A7E }, + { "GreaterTilde;", 0x02273 }, + { "Gscr;", 0x1D4A2 }, + { "Gt;", 0x0226B }, + { "HARDcy;", 0x0042A }, + { "Hacek;", 0x002C7 }, + { "Hat;", 0x0005E }, + { "Hcirc;", 0x00124 }, + { "Hfr;", 0x0210C }, + { "HilbertSpace;", 0x0210B }, + { "Hopf;", 0x0210D }, + { "HorizontalLine;", 0x02500 }, + { "Hscr;", 0x0210B }, + { "Hstrok;", 0x00126 }, + { "HumpDownHump;", 0x0224E }, + { "HumpEqual;", 0x0224F }, + { "IEcy;", 0x00415 }, + { "IJlig;", 0x00132 }, + { "IOcy;", 0x00401 }, + { "Iacute;", 0x000CD }, + { "Iacute", 0x000CD }, + { "Icirc;", 0x000CE }, + { "Icirc", 0x000CE }, + { "Icy;", 0x00418 }, + { "Idot;", 0x00130 }, + { "Ifr;", 0x02111 }, + { "Igrave;", 0x000CC }, + { "Igrave", 0x000CC }, + { "Im;", 0x02111 }, + { "Imacr;", 0x0012A }, + { "ImaginaryI;", 0x02148 }, + { "Implies;", 0x021D2 }, + { "Int;", 0x0222C }, + { "Integral;", 0x0222B }, + { "Intersection;", 0x022C2 }, + { "InvisibleComma;", 0x02063 }, + { "InvisibleTimes;", 0x02062 }, + { "Iogon;", 0x0012E }, + { "Iopf;", 0x1D540 }, + { "Iota;", 0x00399 }, + { "Iscr;", 0x02110 }, + { "Itilde;", 0x00128 }, + { "Iukcy;", 0x00406 }, + { "Iuml;", 0x000CF }, + { "Iuml", 0x000CF }, + { "Jcirc;", 0x00134 }, + { "Jcy;", 0x00419 }, + { "Jfr;", 0x1D50D }, + { "Jopf;", 0x1D541 }, + { "Jscr;", 0x1D4A5 }, + { "Jsercy;", 0x00408 }, + { "Jukcy;", 0x00404 }, + { "KHcy;", 0x00425 }, + { "KJcy;", 0x0040C }, + { "Kappa;", 0x0039A }, + { "Kcedil;", 0x00136 }, + { "Kcy;", 0x0041A }, + { "Kfr;", 0x1D50E }, + { "Kopf;", 0x1D542 }, + { "Kscr;", 0x1D4A6 }, + { "LJcy;", 0x00409 }, + { "LT;", 0x0003C }, + { "LT", 0x0003C }, + { "Lacute;", 0x00139 }, + { "Lambda;", 0x0039B }, + { "Lang;", 0x027EA }, + { "Laplacetrf;", 0x02112 }, + { "Larr;", 0x0219E }, + { "Lcaron;", 0x0013D }, + { "Lcedil;", 0x0013B }, + { "Lcy;", 0x0041B }, + { "LeftAngleBracket;", 0x027E8 }, + { "LeftArrow;", 0x02190 }, + { "LeftArrowBar;", 0x021E4 }, + { "LeftArrowRightArrow;", 0x021C6 }, + { "LeftCeiling;", 0x02308 }, + { "LeftDoubleBracket;", 0x027E6 }, + { "LeftDownTeeVector;", 0x02961 }, + { "LeftDownVector;", 0x021C3 }, + { "LeftDownVectorBar;", 0x02959 }, + { "LeftFloor;", 0x0230A }, + { "LeftRightArrow;", 0x02194 }, + { "LeftRightVector;", 0x0294E }, + { "LeftTee;", 0x022A3 }, + { "LeftTeeArrow;", 0x021A4 }, + { "LeftTeeVector;", 0x0295A }, + { "LeftTriangle;", 0x022B2 }, + { "LeftTriangleBar;", 0x029CF }, + { "LeftTriangleEqual;", 0x022B4 }, + { "LeftUpDownVector;", 0x02951 }, + { "LeftUpTeeVector;", 0x02960 }, + { "LeftUpVector;", 0x021BF }, + { "LeftUpVectorBar;", 0x02958 }, + { "LeftVector;", 0x021BC }, + { "LeftVectorBar;", 0x02952 }, + { "Leftarrow;", 0x021D0 }, + { "Leftrightarrow;", 0x021D4 }, + { "LessEqualGreater;", 0x022DA }, + { "LessFullEqual;", 0x02266 }, + { "LessGreater;", 0x02276 }, + { "LessLess;", 0x02AA1 }, + { "LessSlantEqual;", 0x02A7D }, + { "LessTilde;", 0x02272 }, + { "Lfr;", 0x1D50F }, + { "Ll;", 0x022D8 }, + { "Lleftarrow;", 0x021DA }, + { "Lmidot;", 0x0013F }, + { "LongLeftArrow;", 0x027F5 }, + { "LongLeftRightArrow;", 0x027F7 }, + { "LongRightArrow;", 0x027F6 }, + { "Longleftarrow;", 0x027F8 }, + { "Longleftrightarrow;", 0x027FA }, + { "Longrightarrow;", 0x027F9 }, + { "Lopf;", 0x1D543 }, + { "LowerLeftArrow;", 0x02199 }, + { "LowerRightArrow;", 0x02198 }, + { "Lscr;", 0x02112 }, + { "Lsh;", 0x021B0 }, + { "Lstrok;", 0x00141 }, + { "Lt;", 0x0226A }, + { "Map;", 0x02905 }, + { "Mcy;", 0x0041C }, + { "MediumSpace;", 0x0205F }, + { "Mellintrf;", 0x02133 }, + { "Mfr;", 0x1D510 }, + { "MinusPlus;", 0x02213 }, + { "Mopf;", 0x1D544 }, + { "Mscr;", 0x02133 }, + { "Mu;", 0x0039C }, + { "NJcy;", 0x0040A }, + { "Nacute;", 0x00143 }, + { "Ncaron;", 0x00147 }, + { "Ncedil;", 0x00145 }, + { "Ncy;", 0x0041D }, + { "NegativeMediumSpace;", 0x0200B }, + { "NegativeThickSpace;", 0x0200B }, + { "NegativeThinSpace;", 0x0200B }, + { "NegativeVeryThinSpace;", 0x0200B }, + { "NestedGreaterGreater;", 0x0226B }, + { "NestedLessLess;", 0x0226A }, + { "NewLine;", 0x0000A }, + { "Nfr;", 0x1D511 }, + { "NoBreak;", 0x02060 }, + { "NonBreakingSpace;", 0x000A0 }, + { "Nopf;", 0x02115 }, + { "Not;", 0x02AEC }, + { "NotCongruent;", 0x02262 }, + { "NotCupCap;", 0x0226D }, + { "NotDoubleVerticalBar;", 0x02226 }, + { "NotElement;", 0x02209 }, + { "NotEqual;", 0x02260 }, + { "NotExists;", 0x02204 }, + { "NotGreater;", 0x0226F }, + { "NotGreaterEqual;", 0x02271 }, + { "NotGreaterLess;", 0x02279 }, + { "NotGreaterTilde;", 0x02275 }, + { "NotLeftTriangle;", 0x022EA }, + { "NotLeftTriangleEqual;", 0x022EC }, + { "NotLess;", 0x0226E }, + { "NotLessEqual;", 0x02270 }, + { "NotLessGreater;", 0x02278 }, + { "NotLessTilde;", 0x02274 }, + { "NotPrecedes;", 0x02280 }, + { "NotPrecedesSlantEqual;", 0x022E0 }, + { "NotReverseElement;", 0x0220C }, + { "NotRightTriangle;", 0x022EB }, + { "NotRightTriangleEqual;", 0x022ED }, + { "NotSquareSubsetEqual;", 0x022E2 }, + { "NotSquareSupersetEqual;", 0x022E3 }, + { "NotSubsetEqual;", 0x02288 }, + { "NotSucceeds;", 0x02281 }, + { "NotSucceedsSlantEqual;", 0x022E1 }, + { "NotSupersetEqual;", 0x02289 }, + { "NotTilde;", 0x02241 }, + { "NotTildeEqual;", 0x02244 }, + { "NotTildeFullEqual;", 0x02247 }, + { "NotTildeTilde;", 0x02249 }, + { "NotVerticalBar;", 0x02224 }, + { "Nscr;", 0x1D4A9 }, + { "Ntilde;", 0x000D1 }, + { "Ntilde", 0x000D1 }, + { "Nu;", 0x0039D }, + { "OElig;", 0x00152 }, + { "Oacute;", 0x000D3 }, + { "Oacute", 0x000D3 }, + { "Ocirc;", 0x000D4 }, + { "Ocirc", 0x000D4 }, + { "Ocy;", 0x0041E }, + { "Odblac;", 0x00150 }, + { "Ofr;", 0x1D512 }, + { "Ograve;", 0x000D2 }, + { "Ograve", 0x000D2 }, + { "Omacr;", 0x0014C }, + { "Omega;", 0x003A9 }, + { "Omicron;", 0x0039F }, + { "Oopf;", 0x1D546 }, + { "OpenCurlyDoubleQuote;", 0x0201C }, + { "OpenCurlyQuote;", 0x02018 }, + { "Or;", 0x02A54 }, + { "Oscr;", 0x1D4AA }, + { "Oslash;", 0x000D8 }, + { "Oslash", 0x000D8 }, + { "Otilde;", 0x000D5 }, + { "Otilde", 0x000D5 }, + { "Otimes;", 0x02A37 }, + { "Ouml;", 0x000D6 }, + { "Ouml", 0x000D6 }, + { "OverBar;", 0x0203E }, + { "OverBrace;", 0x023DE }, + { "OverBracket;", 0x023B4 }, + { "OverParenthesis;", 0x023DC }, + { "PartialD;", 0x02202 }, + { "Pcy;", 0x0041F }, + { "Pfr;", 0x1D513 }, + { "Phi;", 0x003A6 }, + { "Pi;", 0x003A0 }, + { "PlusMinus;", 0x000B1 }, + { "Poincareplane;", 0x0210C }, + { "Popf;", 0x02119 }, + { "Pr;", 0x02ABB }, + { "Precedes;", 0x0227A }, + { "PrecedesEqual;", 0x02AAF }, + { "PrecedesSlantEqual;", 0x0227C }, + { "PrecedesTilde;", 0x0227E }, + { "Prime;", 0x02033 }, + { "Product;", 0x0220F }, + { "Proportion;", 0x02237 }, + { "Proportional;", 0x0221D }, + { "Pscr;", 0x1D4AB }, + { "Psi;", 0x003A8 }, + { "QUOT;", 0x00022 }, + { "QUOT", 0x00022 }, + { "Qfr;", 0x1D514 }, + { "Qopf;", 0x0211A }, + { "Qscr;", 0x1D4AC }, + { "RBarr;", 0x02910 }, + { "REG;", 0x000AE }, + { "REG", 0x000AE }, + { "Racute;", 0x00154 }, + { "Rang;", 0x027EB }, + { "Rarr;", 0x021A0 }, + { "Rarrtl;", 0x02916 }, + { "Rcaron;", 0x00158 }, + { "Rcedil;", 0x00156 }, + { "Rcy;", 0x00420 }, + { "Re;", 0x0211C }, + { "ReverseElement;", 0x0220B }, + { "ReverseEquilibrium;", 0x021CB }, + { "ReverseUpEquilibrium;", 0x0296F }, + { "Rfr;", 0x0211C }, + { "Rho;", 0x003A1 }, + { "RightAngleBracket;", 0x027E9 }, + { "RightArrow;", 0x02192 }, + { "RightArrowBar;", 0x021E5 }, + { "RightArrowLeftArrow;", 0x021C4 }, + { "RightCeiling;", 0x02309 }, + { "RightDoubleBracket;", 0x027E7 }, + { "RightDownTeeVector;", 0x0295D }, + { "RightDownVector;", 0x021C2 }, + { "RightDownVectorBar;", 0x02955 }, + { "RightFloor;", 0x0230B }, + { "RightTee;", 0x022A2 }, + { "RightTeeArrow;", 0x021A6 }, + { "RightTeeVector;", 0x0295B }, + { "RightTriangle;", 0x022B3 }, + { "RightTriangleBar;", 0x029D0 }, + { "RightTriangleEqual;", 0x022B5 }, + { "RightUpDownVector;", 0x0294F }, + { "RightUpTeeVector;", 0x0295C }, + { "RightUpVector;", 0x021BE }, + { "RightUpVectorBar;", 0x02954 }, + { "RightVector;", 0x021C0 }, + { "RightVectorBar;", 0x02953 }, + { "Rightarrow;", 0x021D2 }, + { "Ropf;", 0x0211D }, + { "RoundImplies;", 0x02970 }, + { "Rrightarrow;", 0x021DB }, + { "Rscr;", 0x0211B }, + { "Rsh;", 0x021B1 }, + { "RuleDelayed;", 0x029F4 }, + { "SHCHcy;", 0x00429 }, + { "SHcy;", 0x00428 }, + { "SOFTcy;", 0x0042C }, + { "Sacute;", 0x0015A }, + { "Sc;", 0x02ABC }, + { "Scaron;", 0x00160 }, + { "Scedil;", 0x0015E }, + { "Scirc;", 0x0015C }, + { "Scy;", 0x00421 }, + { "Sfr;", 0x1D516 }, + { "ShortDownArrow;", 0x02193 }, + { "ShortLeftArrow;", 0x02190 }, + { "ShortRightArrow;", 0x02192 }, + { "ShortUpArrow;", 0x02191 }, + { "Sigma;", 0x003A3 }, + { "SmallCircle;", 0x02218 }, + { "Sopf;", 0x1D54A }, + { "Sqrt;", 0x0221A }, + { "Square;", 0x025A1 }, + { "SquareIntersection;", 0x02293 }, + { "SquareSubset;", 0x0228F }, + { "SquareSubsetEqual;", 0x02291 }, + { "SquareSuperset;", 0x02290 }, + { "SquareSupersetEqual;", 0x02292 }, + { "SquareUnion;", 0x02294 }, + { "Sscr;", 0x1D4AE }, + { "Star;", 0x022C6 }, + { "Sub;", 0x022D0 }, + { "Subset;", 0x022D0 }, + { "SubsetEqual;", 0x02286 }, + { "Succeeds;", 0x0227B }, + { "SucceedsEqual;", 0x02AB0 }, + { "SucceedsSlantEqual;", 0x0227D }, + { "SucceedsTilde;", 0x0227F }, + { "SuchThat;", 0x0220B }, + { "Sum;", 0x02211 }, + { "Sup;", 0x022D1 }, + { "Superset;", 0x02283 }, + { "SupersetEqual;", 0x02287 }, + { "Supset;", 0x022D1 }, + { "THORN;", 0x000DE }, + { "THORN", 0x000DE }, + { "TRADE;", 0x02122 }, + { "TSHcy;", 0x0040B }, + { "TScy;", 0x00426 }, + { "Tab;", 0x00009 }, + { "Tau;", 0x003A4 }, + { "Tcaron;", 0x00164 }, + { "Tcedil;", 0x00162 }, + { "Tcy;", 0x00422 }, + { "Tfr;", 0x1D517 }, + { "Therefore;", 0x02234 }, + { "Theta;", 0x00398 }, + { "ThinSpace;", 0x02009 }, + { "Tilde;", 0x0223C }, + { "TildeEqual;", 0x02243 }, + { "TildeFullEqual;", 0x02245 }, + { "TildeTilde;", 0x02248 }, + { "Topf;", 0x1D54B }, + { "TripleDot;", 0x020DB }, + { "Tscr;", 0x1D4AF }, + { "Tstrok;", 0x00166 }, + { "Uacute;", 0x000DA }, + { "Uacute", 0x000DA }, + { "Uarr;", 0x0219F }, + { "Uarrocir;", 0x02949 }, + { "Ubrcy;", 0x0040E }, + { "Ubreve;", 0x0016C }, + { "Ucirc;", 0x000DB }, + { "Ucirc", 0x000DB }, + { "Ucy;", 0x00423 }, + { "Udblac;", 0x00170 }, + { "Ufr;", 0x1D518 }, + { "Ugrave;", 0x000D9 }, + { "Ugrave", 0x000D9 }, + { "Umacr;", 0x0016A }, + { "UnderBar;", 0x0005F }, + { "UnderBrace;", 0x023DF }, + { "UnderBracket;", 0x023B5 }, + { "UnderParenthesis;", 0x023DD }, + { "Union;", 0x022C3 }, + { "UnionPlus;", 0x0228E }, + { "Uogon;", 0x00172 }, + { "Uopf;", 0x1D54C }, + { "UpArrow;", 0x02191 }, + { "UpArrowBar;", 0x02912 }, + { "UpArrowDownArrow;", 0x021C5 }, + { "UpDownArrow;", 0x02195 }, + { "UpEquilibrium;", 0x0296E }, + { "UpTee;", 0x022A5 }, + { "UpTeeArrow;", 0x021A5 }, + { "Uparrow;", 0x021D1 }, + { "Updownarrow;", 0x021D5 }, + { "UpperLeftArrow;", 0x02196 }, + { "UpperRightArrow;", 0x02197 }, + { "Upsi;", 0x003D2 }, + { "Upsilon;", 0x003A5 }, + { "Uring;", 0x0016E }, + { "Uscr;", 0x1D4B0 }, + { "Utilde;", 0x00168 }, + { "Uuml;", 0x000DC }, + { "Uuml", 0x000DC }, + { "VDash;", 0x022AB }, + { "Vbar;", 0x02AEB }, + { "Vcy;", 0x00412 }, + { "Vdash;", 0x022A9 }, + { "Vdashl;", 0x02AE6 }, + { "Vee;", 0x022C1 }, + { "Verbar;", 0x02016 }, + { "Vert;", 0x02016 }, + { "VerticalBar;", 0x02223 }, + { "VerticalLine;", 0x0007C }, + { "VerticalSeparator;", 0x02758 }, + { "VerticalTilde;", 0x02240 }, + { "VeryThinSpace;", 0x0200A }, + { "Vfr;", 0x1D519 }, + { "Vopf;", 0x1D54D }, + { "Vscr;", 0x1D4B1 }, + { "Vvdash;", 0x022AA }, + { "Wcirc;", 0x00174 }, + { "Wedge;", 0x022C0 }, + { "Wfr;", 0x1D51A }, + { "Wopf;", 0x1D54E }, + { "Wscr;", 0x1D4B2 }, + { "Xfr;", 0x1D51B }, + { "Xi;", 0x0039E }, + { "Xopf;", 0x1D54F }, + { "Xscr;", 0x1D4B3 }, + { "YAcy;", 0x0042F }, + { "YIcy;", 0x00407 }, + { "YUcy;", 0x0042E }, + { "Yacute;", 0x000DD }, + { "Yacute", 0x000DD }, + { "Ycirc;", 0x00176 }, + { "Ycy;", 0x0042B }, + { "Yfr;", 0x1D51C }, + { "Yopf;", 0x1D550 }, + { "Yscr;", 0x1D4B4 }, + { "Yuml;", 0x00178 }, + { "ZHcy;", 0x00416 }, + { "Zacute;", 0x00179 }, + { "Zcaron;", 0x0017D }, + { "Zcy;", 0x00417 }, + { "Zdot;", 0x0017B }, + { "ZeroWidthSpace;", 0x0200B }, + { "Zeta;", 0x00396 }, + { "Zfr;", 0x02128 }, + { "Zopf;", 0x02124 }, + { "Zscr;", 0x1D4B5 }, + { "aacute;", 0x000E1 }, + { "aacute", 0x000E1 }, + { "abreve;", 0x00103 }, + { "ac;", 0x0223E }, + { "acd;", 0x0223F }, + { "acirc;", 0x000E2 }, + { "acirc", 0x000E2 }, + { "acute;", 0x000B4 }, + { "acute", 0x000B4 }, + { "acy;", 0x00430 }, + { "aelig;", 0x000E6 }, + { "aelig", 0x000E6 }, + { "af;", 0x02061 }, + { "afr;", 0x1D51E }, + { "agrave;", 0x000E0 }, + { "agrave", 0x000E0 }, + { "alefsym;", 0x02135 }, + { "aleph;", 0x02135 }, + { "alpha;", 0x003B1 }, + { "amacr;", 0x00101 }, + { "amalg;", 0x02A3F }, + { "amp;", 0x00026 }, + { "amp", 0x00026 }, + { "and;", 0x02227 }, + { "andand;", 0x02A55 }, + { "andd;", 0x02A5C }, + { "andslope;", 0x02A58 }, + { "andv;", 0x02A5A }, + { "ang;", 0x02220 }, + { "ange;", 0x029A4 }, + { "angle;", 0x02220 }, + { "angmsd;", 0x02221 }, + { "angmsdaa;", 0x029A8 }, + { "angmsdab;", 0x029A9 }, + { "angmsdac;", 0x029AA }, + { "angmsdad;", 0x029AB }, + { "angmsdae;", 0x029AC }, + { "angmsdaf;", 0x029AD }, + { "angmsdag;", 0x029AE }, + { "angmsdah;", 0x029AF }, + { "angrt;", 0x0221F }, + { "angrtvb;", 0x022BE }, + { "angrtvbd;", 0x0299D }, + { "angsph;", 0x02222 }, + { "angst;", 0x000C5 }, + { "angzarr;", 0x0237C }, + { "aogon;", 0x00105 }, + { "aopf;", 0x1D552 }, + { "ap;", 0x02248 }, + { "apE;", 0x02A70 }, + { "apacir;", 0x02A6F }, + { "ape;", 0x0224A }, + { "apid;", 0x0224B }, + { "apos;", 0x00027 }, + { "approx;", 0x02248 }, + { "approxeq;", 0x0224A }, + { "aring;", 0x000E5 }, + { "aring", 0x000E5 }, + { "ascr;", 0x1D4B6 }, + { "ast;", 0x0002A }, + { "asymp;", 0x02248 }, + { "asympeq;", 0x0224D }, + { "atilde;", 0x000E3 }, + { "atilde", 0x000E3 }, + { "auml;", 0x000E4 }, + { "auml", 0x000E4 }, + { "awconint;", 0x02233 }, + { "awint;", 0x02A11 }, + { "bNot;", 0x02AED }, + { "backcong;", 0x0224C }, + { "backepsilon;", 0x003F6 }, + { "backprime;", 0x02035 }, + { "backsim;", 0x0223D }, + { "backsimeq;", 0x022CD }, + { "barvee;", 0x022BD }, + { "barwed;", 0x02305 }, + { "barwedge;", 0x02305 }, + { "bbrk;", 0x023B5 }, + { "bbrktbrk;", 0x023B6 }, + { "bcong;", 0x0224C }, + { "bcy;", 0x00431 }, + { "bdquo;", 0x0201E }, + { "becaus;", 0x02235 }, + { "because;", 0x02235 }, + { "bemptyv;", 0x029B0 }, + { "bepsi;", 0x003F6 }, + { "bernou;", 0x0212C }, + { "beta;", 0x003B2 }, + { "beth;", 0x02136 }, + { "between;", 0x0226C }, + { "bfr;", 0x1D51F }, + { "bigcap;", 0x022C2 }, + { "bigcirc;", 0x025EF }, + { "bigcup;", 0x022C3 }, + { "bigodot;", 0x02A00 }, + { "bigoplus;", 0x02A01 }, + { "bigotimes;", 0x02A02 }, + { "bigsqcup;", 0x02A06 }, + { "bigstar;", 0x02605 }, + { "bigtriangledown;", 0x025BD }, + { "bigtriangleup;", 0x025B3 }, + { "biguplus;", 0x02A04 }, + { "bigvee;", 0x022C1 }, + { "bigwedge;", 0x022C0 }, + { "bkarow;", 0x0290D }, + { "blacklozenge;", 0x029EB }, + { "blacksquare;", 0x025AA }, + { "blacktriangle;", 0x025B4 }, + { "blacktriangledown;", 0x025BE }, + { "blacktriangleleft;", 0x025C2 }, + { "blacktriangleright;", 0x025B8 }, + { "blank;", 0x02423 }, + { "blk12;", 0x02592 }, + { "blk14;", 0x02591 }, + { "blk34;", 0x02593 }, + { "block;", 0x02588 }, + { "bnot;", 0x02310 }, + { "bopf;", 0x1D553 }, + { "bot;", 0x022A5 }, + { "bottom;", 0x022A5 }, + { "bowtie;", 0x022C8 }, + { "boxDL;", 0x02557 }, + { "boxDR;", 0x02554 }, + { "boxDl;", 0x02556 }, + { "boxDr;", 0x02553 }, + { "boxH;", 0x02550 }, + { "boxHD;", 0x02566 }, + { "boxHU;", 0x02569 }, + { "boxHd;", 0x02564 }, + { "boxHu;", 0x02567 }, + { "boxUL;", 0x0255D }, + { "boxUR;", 0x0255A }, + { "boxUl;", 0x0255C }, + { "boxUr;", 0x02559 }, + { "boxV;", 0x02551 }, + { "boxVH;", 0x0256C }, + { "boxVL;", 0x02563 }, + { "boxVR;", 0x02560 }, + { "boxVh;", 0x0256B }, + { "boxVl;", 0x02562 }, + { "boxVr;", 0x0255F }, + { "boxbox;", 0x029C9 }, + { "boxdL;", 0x02555 }, + { "boxdR;", 0x02552 }, + { "boxdl;", 0x02510 }, + { "boxdr;", 0x0250C }, + { "boxh;", 0x02500 }, + { "boxhD;", 0x02565 }, + { "boxhU;", 0x02568 }, + { "boxhd;", 0x0252C }, + { "boxhu;", 0x02534 }, + { "boxminus;", 0x0229F }, + { "boxplus;", 0x0229E }, + { "boxtimes;", 0x022A0 }, + { "boxuL;", 0x0255B }, + { "boxuR;", 0x02558 }, + { "boxul;", 0x02518 }, + { "boxur;", 0x02514 }, + { "boxv;", 0x02502 }, + { "boxvH;", 0x0256A }, + { "boxvL;", 0x02561 }, + { "boxvR;", 0x0255E }, + { "boxvh;", 0x0253C }, + { "boxvl;", 0x02524 }, + { "boxvr;", 0x0251C }, + { "bprime;", 0x02035 }, + { "breve;", 0x002D8 }, + { "brvbar;", 0x000A6 }, + { "brvbar", 0x000A6 }, + { "bscr;", 0x1D4B7 }, + { "bsemi;", 0x0204F }, + { "bsim;", 0x0223D }, + { "bsime;", 0x022CD }, + { "bsol;", 0x0005C }, + { "bsolb;", 0x029C5 }, + { "bsolhsub;", 0x027C8 }, + { "bull;", 0x02022 }, + { "bullet;", 0x02022 }, + { "bump;", 0x0224E }, + { "bumpE;", 0x02AAE }, + { "bumpe;", 0x0224F }, + { "bumpeq;", 0x0224F }, + { "cacute;", 0x00107 }, + { "cap;", 0x02229 }, + { "capand;", 0x02A44 }, + { "capbrcup;", 0x02A49 }, + { "capcap;", 0x02A4B }, + { "capcup;", 0x02A47 }, + { "capdot;", 0x02A40 }, + { "caret;", 0x02041 }, + { "caron;", 0x002C7 }, + { "ccaps;", 0x02A4D }, + { "ccaron;", 0x0010D }, + { "ccedil;", 0x000E7 }, + { "ccedil", 0x000E7 }, + { "ccirc;", 0x00109 }, + { "ccups;", 0x02A4C }, + { "ccupssm;", 0x02A50 }, + { "cdot;", 0x0010B }, + { "cedil;", 0x000B8 }, + { "cedil", 0x000B8 }, + { "cemptyv;", 0x029B2 }, + { "cent;", 0x000A2 }, + { "cent", 0x000A2 }, + { "centerdot;", 0x000B7 }, + { "cfr;", 0x1D520 }, + { "chcy;", 0x00447 }, + { "check;", 0x02713 }, + { "checkmark;", 0x02713 }, + { "chi;", 0x003C7 }, + { "cir;", 0x025CB }, + { "cirE;", 0x029C3 }, + { "circ;", 0x002C6 }, + { "circeq;", 0x02257 }, + { "circlearrowleft;", 0x021BA }, + { "circlearrowright;", 0x021BB }, + { "circledR;", 0x000AE }, + { "circledS;", 0x024C8 }, + { "circledast;", 0x0229B }, + { "circledcirc;", 0x0229A }, + { "circleddash;", 0x0229D }, + { "cire;", 0x02257 }, + { "cirfnint;", 0x02A10 }, + { "cirmid;", 0x02AEF }, + { "cirscir;", 0x029C2 }, + { "clubs;", 0x02663 }, + { "clubsuit;", 0x02663 }, + { "colon;", 0x0003A }, + { "colone;", 0x02254 }, + { "coloneq;", 0x02254 }, + { "comma;", 0x0002C }, + { "commat;", 0x00040 }, + { "comp;", 0x02201 }, + { "compfn;", 0x02218 }, + { "complement;", 0x02201 }, + { "complexes;", 0x02102 }, + { "cong;", 0x02245 }, + { "congdot;", 0x02A6D }, + { "conint;", 0x0222E }, + { "copf;", 0x1D554 }, + { "coprod;", 0x02210 }, + { "copy;", 0x000A9 }, + { "copy", 0x000A9 }, + { "copysr;", 0x02117 }, + { "crarr;", 0x021B5 }, + { "cross;", 0x02717 }, + { "cscr;", 0x1D4B8 }, + { "csub;", 0x02ACF }, + { "csube;", 0x02AD1 }, + { "csup;", 0x02AD0 }, + { "csupe;", 0x02AD2 }, + { "ctdot;", 0x022EF }, + { "cudarrl;", 0x02938 }, + { "cudarrr;", 0x02935 }, + { "cuepr;", 0x022DE }, + { "cuesc;", 0x022DF }, + { "cularr;", 0x021B6 }, + { "cularrp;", 0x0293D }, + { "cup;", 0x0222A }, + { "cupbrcap;", 0x02A48 }, + { "cupcap;", 0x02A46 }, + { "cupcup;", 0x02A4A }, + { "cupdot;", 0x0228D }, + { "cupor;", 0x02A45 }, + { "curarr;", 0x021B7 }, + { "curarrm;", 0x0293C }, + { "curlyeqprec;", 0x022DE }, + { "curlyeqsucc;", 0x022DF }, + { "curlyvee;", 0x022CE }, + { "curlywedge;", 0x022CF }, + { "curren;", 0x000A4 }, + { "curren", 0x000A4 }, + { "curvearrowleft;", 0x021B6 }, + { "curvearrowright;", 0x021B7 }, + { "cuvee;", 0x022CE }, + { "cuwed;", 0x022CF }, + { "cwconint;", 0x02232 }, + { "cwint;", 0x02231 }, + { "cylcty;", 0x0232D }, + { "dArr;", 0x021D3 }, + { "dHar;", 0x02965 }, + { "dagger;", 0x02020 }, + { "daleth;", 0x02138 }, + { "darr;", 0x02193 }, + { "dash;", 0x02010 }, + { "dashv;", 0x022A3 }, + { "dbkarow;", 0x0290F }, + { "dblac;", 0x002DD }, + { "dcaron;", 0x0010F }, + { "dcy;", 0x00434 }, + { "dd;", 0x02146 }, + { "ddagger;", 0x02021 }, + { "ddarr;", 0x021CA }, + { "ddotseq;", 0x02A77 }, + { "deg;", 0x000B0 }, + { "deg", 0x000B0 }, + { "delta;", 0x003B4 }, + { "demptyv;", 0x029B1 }, + { "dfisht;", 0x0297F }, + { "dfr;", 0x1D521 }, + { "dharl;", 0x021C3 }, + { "dharr;", 0x021C2 }, + { "diam;", 0x022C4 }, + { "diamond;", 0x022C4 }, + { "diamondsuit;", 0x02666 }, + { "diams;", 0x02666 }, + { "die;", 0x000A8 }, + { "digamma;", 0x003DD }, + { "disin;", 0x022F2 }, + { "div;", 0x000F7 }, + { "divide;", 0x000F7 }, + { "divide", 0x000F7 }, + { "divideontimes;", 0x022C7 }, + { "divonx;", 0x022C7 }, + { "djcy;", 0x00452 }, + { "dlcorn;", 0x0231E }, + { "dlcrop;", 0x0230D }, + { "dollar;", 0x00024 }, + { "dopf;", 0x1D555 }, + { "dot;", 0x002D9 }, + { "doteq;", 0x02250 }, + { "doteqdot;", 0x02251 }, + { "dotminus;", 0x02238 }, + { "dotplus;", 0x02214 }, + { "dotsquare;", 0x022A1 }, + { "doublebarwedge;", 0x02306 }, + { "downarrow;", 0x02193 }, + { "downdownarrows;", 0x021CA }, + { "downharpoonleft;", 0x021C3 }, + { "downharpoonright;", 0x021C2 }, + { "drbkarow;", 0x02910 }, + { "drcorn;", 0x0231F }, + { "drcrop;", 0x0230C }, + { "dscr;", 0x1D4B9 }, + { "dscy;", 0x00455 }, + { "dsol;", 0x029F6 }, + { "dstrok;", 0x00111 }, + { "dtdot;", 0x022F1 }, + { "dtri;", 0x025BF }, + { "dtrif;", 0x025BE }, + { "duarr;", 0x021F5 }, + { "duhar;", 0x0296F }, + { "dwangle;", 0x029A6 }, + { "dzcy;", 0x0045F }, + { "dzigrarr;", 0x027FF }, + { "eDDot;", 0x02A77 }, + { "eDot;", 0x02251 }, + { "eacute;", 0x000E9 }, + { "eacute", 0x000E9 }, + { "easter;", 0x02A6E }, + { "ecaron;", 0x0011B }, + { "ecir;", 0x02256 }, + { "ecirc;", 0x000EA }, + { "ecirc", 0x000EA }, + { "ecolon;", 0x02255 }, + { "ecy;", 0x0044D }, + { "edot;", 0x00117 }, + { "ee;", 0x02147 }, + { "efDot;", 0x02252 }, + { "efr;", 0x1D522 }, + { "eg;", 0x02A9A }, + { "egrave;", 0x000E8 }, + { "egrave", 0x000E8 }, + { "egs;", 0x02A96 }, + { "egsdot;", 0x02A98 }, + { "el;", 0x02A99 }, + { "elinters;", 0x023E7 }, + { "ell;", 0x02113 }, + { "els;", 0x02A95 }, + { "elsdot;", 0x02A97 }, + { "emacr;", 0x00113 }, + { "empty;", 0x02205 }, + { "emptyset;", 0x02205 }, + { "emptyv;", 0x02205 }, + { "emsp13;", 0x02004 }, + { "emsp14;", 0x02005 }, + { "emsp;", 0x02003 }, + { "eng;", 0x0014B }, + { "ensp;", 0x02002 }, + { "eogon;", 0x00119 }, + { "eopf;", 0x1D556 }, + { "epar;", 0x022D5 }, + { "eparsl;", 0x029E3 }, + { "eplus;", 0x02A71 }, + { "epsi;", 0x003B5 }, + { "epsilon;", 0x003B5 }, + { "epsiv;", 0x003F5 }, + { "eqcirc;", 0x02256 }, + { "eqcolon;", 0x02255 }, + { "eqsim;", 0x02242 }, + { "eqslantgtr;", 0x02A96 }, + { "eqslantless;", 0x02A95 }, + { "equals;", 0x0003D }, + { "equest;", 0x0225F }, + { "equiv;", 0x02261 }, + { "equivDD;", 0x02A78 }, + { "eqvparsl;", 0x029E5 }, + { "erDot;", 0x02253 }, + { "erarr;", 0x02971 }, + { "escr;", 0x0212F }, + { "esdot;", 0x02250 }, + { "esim;", 0x02242 }, + { "eta;", 0x003B7 }, + { "eth;", 0x000F0 }, + { "eth", 0x000F0 }, + { "euml;", 0x000EB }, + { "euml", 0x000EB }, + { "euro;", 0x020AC }, + { "excl;", 0x00021 }, + { "exist;", 0x02203 }, + { "expectation;", 0x02130 }, + { "exponentiale;", 0x02147 }, + { "fallingdotseq;", 0x02252 }, + { "fcy;", 0x00444 }, + { "female;", 0x02640 }, + { "ffilig;", 0x0FB03 }, + { "fflig;", 0x0FB00 }, + { "ffllig;", 0x0FB04 }, + { "ffr;", 0x1D523 }, + { "filig;", 0x0FB01 }, + { "flat;", 0x0266D }, + { "fllig;", 0x0FB02 }, + { "fltns;", 0x025B1 }, + { "fnof;", 0x00192 }, + { "fopf;", 0x1D557 }, + { "forall;", 0x02200 }, + { "fork;", 0x022D4 }, + { "forkv;", 0x02AD9 }, + { "fpartint;", 0x02A0D }, + { "frac12;", 0x000BD }, + { "frac12", 0x000BD }, + { "frac13;", 0x02153 }, + { "frac14;", 0x000BC }, + { "frac14", 0x000BC }, + { "frac15;", 0x02155 }, + { "frac16;", 0x02159 }, + { "frac18;", 0x0215B }, + { "frac23;", 0x02154 }, + { "frac25;", 0x02156 }, + { "frac34;", 0x000BE }, + { "frac34", 0x000BE }, + { "frac35;", 0x02157 }, + { "frac38;", 0x0215C }, + { "frac45;", 0x02158 }, + { "frac56;", 0x0215A }, + { "frac58;", 0x0215D }, + { "frac78;", 0x0215E }, + { "frasl;", 0x02044 }, + { "frown;", 0x02322 }, + { "fscr;", 0x1D4BB }, + { "gE;", 0x02267 }, + { "gEl;", 0x02A8C }, + { "gacute;", 0x001F5 }, + { "gamma;", 0x003B3 }, + { "gammad;", 0x003DD }, + { "gap;", 0x02A86 }, + { "gbreve;", 0x0011F }, + { "gcirc;", 0x0011D }, + { "gcy;", 0x00433 }, + { "gdot;", 0x00121 }, + { "ge;", 0x02265 }, + { "gel;", 0x022DB }, + { "geq;", 0x02265 }, + { "geqq;", 0x02267 }, + { "geqslant;", 0x02A7E }, + { "ges;", 0x02A7E }, + { "gescc;", 0x02AA9 }, + { "gesdot;", 0x02A80 }, + { "gesdoto;", 0x02A82 }, + { "gesdotol;", 0x02A84 }, + { "gesles;", 0x02A94 }, + { "gfr;", 0x1D524 }, + { "gg;", 0x0226B }, + { "ggg;", 0x022D9 }, + { "gimel;", 0x02137 }, + { "gjcy;", 0x00453 }, + { "gl;", 0x02277 }, + { "glE;", 0x02A92 }, + { "gla;", 0x02AA5 }, + { "glj;", 0x02AA4 }, + { "gnE;", 0x02269 }, + { "gnap;", 0x02A8A }, + { "gnapprox;", 0x02A8A }, + { "gne;", 0x02A88 }, + { "gneq;", 0x02A88 }, + { "gneqq;", 0x02269 }, + { "gnsim;", 0x022E7 }, + { "gopf;", 0x1D558 }, + { "grave;", 0x00060 }, + { "gscr;", 0x0210A }, + { "gsim;", 0x02273 }, + { "gsime;", 0x02A8E }, + { "gsiml;", 0x02A90 }, + { "gt;", 0x0003E }, + { "gt", 0x0003E }, + { "gtcc;", 0x02AA7 }, + { "gtcir;", 0x02A7A }, + { "gtdot;", 0x022D7 }, + { "gtlPar;", 0x02995 }, + { "gtquest;", 0x02A7C }, + { "gtrapprox;", 0x02A86 }, + { "gtrarr;", 0x02978 }, + { "gtrdot;", 0x022D7 }, + { "gtreqless;", 0x022DB }, + { "gtreqqless;", 0x02A8C }, + { "gtrless;", 0x02277 }, + { "gtrsim;", 0x02273 }, + { "hArr;", 0x021D4 }, + { "hairsp;", 0x0200A }, + { "half;", 0x000BD }, + { "hamilt;", 0x0210B }, + { "hardcy;", 0x0044A }, + { "harr;", 0x02194 }, + { "harrcir;", 0x02948 }, + { "harrw;", 0x021AD }, + { "hbar;", 0x0210F }, + { "hcirc;", 0x00125 }, + { "hearts;", 0x02665 }, + { "heartsuit;", 0x02665 }, + { "hellip;", 0x02026 }, + { "hercon;", 0x022B9 }, + { "hfr;", 0x1D525 }, + { "hksearow;", 0x02925 }, + { "hkswarow;", 0x02926 }, + { "hoarr;", 0x021FF }, + { "homtht;", 0x0223B }, + { "hookleftarrow;", 0x021A9 }, + { "hookrightarrow;", 0x021AA }, + { "hopf;", 0x1D559 }, + { "horbar;", 0x02015 }, + { "hscr;", 0x1D4BD }, + { "hslash;", 0x0210F }, + { "hstrok;", 0x00127 }, + { "hybull;", 0x02043 }, + { "hyphen;", 0x02010 }, + { "iacute;", 0x000ED }, + { "iacute", 0x000ED }, + { "ic;", 0x02063 }, + { "icirc;", 0x000EE }, + { "icirc", 0x000EE }, + { "icy;", 0x00438 }, + { "iecy;", 0x00435 }, + { "iexcl;", 0x000A1 }, + { "iexcl", 0x000A1 }, + { "iff;", 0x021D4 }, + { "ifr;", 0x1D526 }, + { "igrave;", 0x000EC }, + { "igrave", 0x000EC }, + { "ii;", 0x02148 }, + { "iiiint;", 0x02A0C }, + { "iiint;", 0x0222D }, + { "iinfin;", 0x029DC }, + { "iiota;", 0x02129 }, + { "ijlig;", 0x00133 }, + { "imacr;", 0x0012B }, + { "image;", 0x02111 }, + { "imagline;", 0x02110 }, + { "imagpart;", 0x02111 }, + { "imath;", 0x00131 }, + { "imof;", 0x022B7 }, + { "imped;", 0x001B5 }, + { "in;", 0x02208 }, + { "incare;", 0x02105 }, + { "infin;", 0x0221E }, + { "infintie;", 0x029DD }, + { "inodot;", 0x00131 }, + { "int;", 0x0222B }, + { "intcal;", 0x022BA }, + { "integers;", 0x02124 }, + { "intercal;", 0x022BA }, + { "intlarhk;", 0x02A17 }, + { "intprod;", 0x02A3C }, + { "iocy;", 0x00451 }, + { "iogon;", 0x0012F }, + { "iopf;", 0x1D55A }, + { "iota;", 0x003B9 }, + { "iprod;", 0x02A3C }, + { "iquest;", 0x000BF }, + { "iquest", 0x000BF }, + { "iscr;", 0x1D4BE }, + { "isin;", 0x02208 }, + { "isinE;", 0x022F9 }, + { "isindot;", 0x022F5 }, + { "isins;", 0x022F4 }, + { "isinsv;", 0x022F3 }, + { "isinv;", 0x02208 }, + { "it;", 0x02062 }, + { "itilde;", 0x00129 }, + { "iukcy;", 0x00456 }, + { "iuml;", 0x000EF }, + { "iuml", 0x000EF }, + { "jcirc;", 0x00135 }, + { "jcy;", 0x00439 }, + { "jfr;", 0x1D527 }, + { "jmath;", 0x00237 }, + { "jopf;", 0x1D55B }, + { "jscr;", 0x1D4BF }, + { "jsercy;", 0x00458 }, + { "jukcy;", 0x00454 }, + { "kappa;", 0x003BA }, + { "kappav;", 0x003F0 }, + { "kcedil;", 0x00137 }, + { "kcy;", 0x0043A }, + { "kfr;", 0x1D528 }, + { "kgreen;", 0x00138 }, + { "khcy;", 0x00445 }, + { "kjcy;", 0x0045C }, + { "kopf;", 0x1D55C }, + { "kscr;", 0x1D4C0 }, + { "lAarr;", 0x021DA }, + { "lArr;", 0x021D0 }, + { "lAtail;", 0x0291B }, + { "lBarr;", 0x0290E }, + { "lE;", 0x02266 }, + { "lEg;", 0x02A8B }, + { "lHar;", 0x02962 }, + { "lacute;", 0x0013A }, + { "laemptyv;", 0x029B4 }, + { "lagran;", 0x02112 }, + { "lambda;", 0x003BB }, + { "lang;", 0x027E8 }, + { "langd;", 0x02991 }, + { "langle;", 0x027E8 }, + { "lap;", 0x02A85 }, + { "laquo;", 0x000AB }, + { "laquo", 0x000AB }, + { "larr;", 0x02190 }, + { "larrb;", 0x021E4 }, + { "larrbfs;", 0x0291F }, + { "larrfs;", 0x0291D }, + { "larrhk;", 0x021A9 }, + { "larrlp;", 0x021AB }, + { "larrpl;", 0x02939 }, + { "larrsim;", 0x02973 }, + { "larrtl;", 0x021A2 }, + { "lat;", 0x02AAB }, + { "latail;", 0x02919 }, + { "late;", 0x02AAD }, + { "lbarr;", 0x0290C }, + { "lbbrk;", 0x02772 }, + { "lbrace;", 0x0007B }, + { "lbrack;", 0x0005B }, + { "lbrke;", 0x0298B }, + { "lbrksld;", 0x0298F }, + { "lbrkslu;", 0x0298D }, + { "lcaron;", 0x0013E }, + { "lcedil;", 0x0013C }, + { "lceil;", 0x02308 }, + { "lcub;", 0x0007B }, + { "lcy;", 0x0043B }, + { "ldca;", 0x02936 }, + { "ldquo;", 0x0201C }, + { "ldquor;", 0x0201E }, + { "ldrdhar;", 0x02967 }, + { "ldrushar;", 0x0294B }, + { "ldsh;", 0x021B2 }, + { "le;", 0x02264 }, + { "leftarrow;", 0x02190 }, + { "leftarrowtail;", 0x021A2 }, + { "leftharpoondown;", 0x021BD }, + { "leftharpoonup;", 0x021BC }, + { "leftleftarrows;", 0x021C7 }, + { "leftrightarrow;", 0x02194 }, + { "leftrightarrows;", 0x021C6 }, + { "leftrightharpoons;", 0x021CB }, + { "leftrightsquigarrow;", 0x021AD }, + { "leftthreetimes;", 0x022CB }, + { "leg;", 0x022DA }, + { "leq;", 0x02264 }, + { "leqq;", 0x02266 }, + { "leqslant;", 0x02A7D }, + { "les;", 0x02A7D }, + { "lescc;", 0x02AA8 }, + { "lesdot;", 0x02A7F }, + { "lesdoto;", 0x02A81 }, + { "lesdotor;", 0x02A83 }, + { "lesges;", 0x02A93 }, + { "lessapprox;", 0x02A85 }, + { "lessdot;", 0x022D6 }, + { "lesseqgtr;", 0x022DA }, + { "lesseqqgtr;", 0x02A8B }, + { "lessgtr;", 0x02276 }, + { "lesssim;", 0x02272 }, + { "lfisht;", 0x0297C }, + { "lfloor;", 0x0230A }, + { "lfr;", 0x1D529 }, + { "lg;", 0x02276 }, + { "lgE;", 0x02A91 }, + { "lhard;", 0x021BD }, + { "lharu;", 0x021BC }, + { "lharul;", 0x0296A }, + { "lhblk;", 0x02584 }, + { "ljcy;", 0x00459 }, + { "ll;", 0x0226A }, + { "llarr;", 0x021C7 }, + { "llcorner;", 0x0231E }, + { "llhard;", 0x0296B }, + { "lltri;", 0x025FA }, + { "lmidot;", 0x00140 }, + { "lmoust;", 0x023B0 }, + { "lmoustache;", 0x023B0 }, + { "lnE;", 0x02268 }, + { "lnap;", 0x02A89 }, + { "lnapprox;", 0x02A89 }, + { "lne;", 0x02A87 }, + { "lneq;", 0x02A87 }, + { "lneqq;", 0x02268 }, + { "lnsim;", 0x022E6 }, + { "loang;", 0x027EC }, + { "loarr;", 0x021FD }, + { "lobrk;", 0x027E6 }, + { "longleftarrow;", 0x027F5 }, + { "longleftrightarrow;", 0x027F7 }, + { "longmapsto;", 0x027FC }, + { "longrightarrow;", 0x027F6 }, + { "looparrowleft;", 0x021AB }, + { "looparrowright;", 0x021AC }, + { "lopar;", 0x02985 }, + { "lopf;", 0x1D55D }, + { "loplus;", 0x02A2D }, + { "lotimes;", 0x02A34 }, + { "lowast;", 0x02217 }, + { "lowbar;", 0x0005F }, + { "loz;", 0x025CA }, + { "lozenge;", 0x025CA }, + { "lozf;", 0x029EB }, + { "lpar;", 0x00028 }, + { "lparlt;", 0x02993 }, + { "lrarr;", 0x021C6 }, + { "lrcorner;", 0x0231F }, + { "lrhar;", 0x021CB }, + { "lrhard;", 0x0296D }, + { "lrm;", 0x0200E }, + { "lrtri;", 0x022BF }, + { "lsaquo;", 0x02039 }, + { "lscr;", 0x1D4C1 }, + { "lsh;", 0x021B0 }, + { "lsim;", 0x02272 }, + { "lsime;", 0x02A8D }, + { "lsimg;", 0x02A8F }, + { "lsqb;", 0x0005B }, + { "lsquo;", 0x02018 }, + { "lsquor;", 0x0201A }, + { "lstrok;", 0x00142 }, + { "lt;", 0x0003C }, + { "lt", 0x0003C }, + { "ltcc;", 0x02AA6 }, + { "ltcir;", 0x02A79 }, + { "ltdot;", 0x022D6 }, + { "lthree;", 0x022CB }, + { "ltimes;", 0x022C9 }, + { "ltlarr;", 0x02976 }, + { "ltquest;", 0x02A7B }, + { "ltrPar;", 0x02996 }, + { "ltri;", 0x025C3 }, + { "ltrie;", 0x022B4 }, + { "ltrif;", 0x025C2 }, + { "lurdshar;", 0x0294A }, + { "luruhar;", 0x02966 }, + { "mDDot;", 0x0223A }, + { "macr;", 0x000AF }, + { "macr", 0x000AF }, + { "male;", 0x02642 }, + { "malt;", 0x02720 }, + { "maltese;", 0x02720 }, + { "map;", 0x021A6 }, + { "mapsto;", 0x021A6 }, + { "mapstodown;", 0x021A7 }, + { "mapstoleft;", 0x021A4 }, + { "mapstoup;", 0x021A5 }, + { "marker;", 0x025AE }, + { "mcomma;", 0x02A29 }, + { "mcy;", 0x0043C }, + { "mdash;", 0x02014 }, + { "measuredangle;", 0x02221 }, + { "mfr;", 0x1D52A }, + { "mho;", 0x02127 }, + { "micro;", 0x000B5 }, + { "micro", 0x000B5 }, + { "mid;", 0x02223 }, + { "midast;", 0x0002A }, + { "midcir;", 0x02AF0 }, + { "middot;", 0x000B7 }, + { "middot", 0x000B7 }, + { "minus;", 0x02212 }, + { "minusb;", 0x0229F }, + { "minusd;", 0x02238 }, + { "minusdu;", 0x02A2A }, + { "mlcp;", 0x02ADB }, + { "mldr;", 0x02026 }, + { "mnplus;", 0x02213 }, + { "models;", 0x022A7 }, + { "mopf;", 0x1D55E }, + { "mp;", 0x02213 }, + { "mscr;", 0x1D4C2 }, + { "mstpos;", 0x0223E }, + { "mu;", 0x003BC }, + { "multimap;", 0x022B8 }, + { "mumap;", 0x022B8 }, + { "nLeftarrow;", 0x021CD }, + { "nLeftrightarrow;", 0x021CE }, + { "nRightarrow;", 0x021CF }, + { "nVDash;", 0x022AF }, + { "nVdash;", 0x022AE }, + { "nabla;", 0x02207 }, + { "nacute;", 0x00144 }, + { "nap;", 0x02249 }, + { "napos;", 0x00149 }, + { "napprox;", 0x02249 }, + { "natur;", 0x0266E }, + { "natural;", 0x0266E }, + { "naturals;", 0x02115 }, + { "nbsp;", 0x000A0 }, + { "nbsp", 0x000A0 }, + { "ncap;", 0x02A43 }, + { "ncaron;", 0x00148 }, + { "ncedil;", 0x00146 }, + { "ncong;", 0x02247 }, + { "ncup;", 0x02A42 }, + { "ncy;", 0x0043D }, + { "ndash;", 0x02013 }, + { "ne;", 0x02260 }, + { "neArr;", 0x021D7 }, + { "nearhk;", 0x02924 }, + { "nearr;", 0x02197 }, + { "nearrow;", 0x02197 }, + { "nequiv;", 0x02262 }, + { "nesear;", 0x02928 }, + { "nexist;", 0x02204 }, + { "nexists;", 0x02204 }, + { "nfr;", 0x1D52B }, + { "nge;", 0x02271 }, + { "ngeq;", 0x02271 }, + { "ngsim;", 0x02275 }, + { "ngt;", 0x0226F }, + { "ngtr;", 0x0226F }, + { "nhArr;", 0x021CE }, + { "nharr;", 0x021AE }, + { "nhpar;", 0x02AF2 }, + { "ni;", 0x0220B }, + { "nis;", 0x022FC }, + { "nisd;", 0x022FA }, + { "niv;", 0x0220B }, + { "njcy;", 0x0045A }, + { "nlArr;", 0x021CD }, + { "nlarr;", 0x0219A }, + { "nldr;", 0x02025 }, + { "nle;", 0x02270 }, + { "nleftarrow;", 0x0219A }, + { "nleftrightarrow;", 0x021AE }, + { "nleq;", 0x02270 }, + { "nless;", 0x0226E }, + { "nlsim;", 0x02274 }, + { "nlt;", 0x0226E }, + { "nltri;", 0x022EA }, + { "nltrie;", 0x022EC }, + { "nmid;", 0x02224 }, + { "nopf;", 0x1D55F }, + { "not;", 0x000AC }, + { "not", 0x000AC }, + { "notin;", 0x02209 }, + { "notinva;", 0x02209 }, + { "notinvb;", 0x022F7 }, + { "notinvc;", 0x022F6 }, + { "notni;", 0x0220C }, + { "notniva;", 0x0220C }, + { "notnivb;", 0x022FE }, + { "notnivc;", 0x022FD }, + { "npar;", 0x02226 }, + { "nparallel;", 0x02226 }, + { "npolint;", 0x02A14 }, + { "npr;", 0x02280 }, + { "nprcue;", 0x022E0 }, + { "nprec;", 0x02280 }, + { "nrArr;", 0x021CF }, + { "nrarr;", 0x0219B }, + { "nrightarrow;", 0x0219B }, + { "nrtri;", 0x022EB }, + { "nrtrie;", 0x022ED }, + { "nsc;", 0x02281 }, + { "nsccue;", 0x022E1 }, + { "nscr;", 0x1D4C3 }, + { "nshortmid;", 0x02224 }, + { "nshortparallel;", 0x02226 }, + { "nsim;", 0x02241 }, + { "nsime;", 0x02244 }, + { "nsimeq;", 0x02244 }, + { "nsmid;", 0x02224 }, + { "nspar;", 0x02226 }, + { "nsqsube;", 0x022E2 }, + { "nsqsupe;", 0x022E3 }, + { "nsub;", 0x02284 }, + { "nsube;", 0x02288 }, + { "nsubseteq;", 0x02288 }, + { "nsucc;", 0x02281 }, + { "nsup;", 0x02285 }, + { "nsupe;", 0x02289 }, + { "nsupseteq;", 0x02289 }, + { "ntgl;", 0x02279 }, + { "ntilde;", 0x000F1 }, + { "ntilde", 0x000F1 }, + { "ntlg;", 0x02278 }, + { "ntriangleleft;", 0x022EA }, + { "ntrianglelefteq;", 0x022EC }, + { "ntriangleright;", 0x022EB }, + { "ntrianglerighteq;", 0x022ED }, + { "nu;", 0x003BD }, + { "num;", 0x00023 }, + { "numero;", 0x02116 }, + { "numsp;", 0x02007 }, + { "nvDash;", 0x022AD }, + { "nvHarr;", 0x02904 }, + { "nvdash;", 0x022AC }, + { "nvinfin;", 0x029DE }, + { "nvlArr;", 0x02902 }, + { "nvrArr;", 0x02903 }, + { "nwArr;", 0x021D6 }, + { "nwarhk;", 0x02923 }, + { "nwarr;", 0x02196 }, + { "nwarrow;", 0x02196 }, + { "nwnear;", 0x02927 }, + { "oS;", 0x024C8 }, + { "oacute;", 0x000F3 }, + { "oacute", 0x000F3 }, + { "oast;", 0x0229B }, + { "ocir;", 0x0229A }, + { "ocirc;", 0x000F4 }, + { "ocirc", 0x000F4 }, + { "ocy;", 0x0043E }, + { "odash;", 0x0229D }, + { "odblac;", 0x00151 }, + { "odiv;", 0x02A38 }, + { "odot;", 0x02299 }, + { "odsold;", 0x029BC }, + { "oelig;", 0x00153 }, + { "ofcir;", 0x029BF }, + { "ofr;", 0x1D52C }, + { "ogon;", 0x002DB }, + { "ograve;", 0x000F2 }, + { "ograve", 0x000F2 }, + { "ogt;", 0x029C1 }, + { "ohbar;", 0x029B5 }, + { "ohm;", 0x003A9 }, + { "oint;", 0x0222E }, + { "olarr;", 0x021BA }, + { "olcir;", 0x029BE }, + { "olcross;", 0x029BB }, + { "oline;", 0x0203E }, + { "olt;", 0x029C0 }, + { "omacr;", 0x0014D }, + { "omega;", 0x003C9 }, + { "omicron;", 0x003BF }, + { "omid;", 0x029B6 }, + { "ominus;", 0x02296 }, + { "oopf;", 0x1D560 }, + { "opar;", 0x029B7 }, + { "operp;", 0x029B9 }, + { "oplus;", 0x02295 }, + { "or;", 0x02228 }, + { "orarr;", 0x021BB }, + { "ord;", 0x02A5D }, + { "order;", 0x02134 }, + { "orderof;", 0x02134 }, + { "ordf;", 0x000AA }, + { "ordf", 0x000AA }, + { "ordm;", 0x000BA }, + { "ordm", 0x000BA }, + { "origof;", 0x022B6 }, + { "oror;", 0x02A56 }, + { "orslope;", 0x02A57 }, + { "orv;", 0x02A5B }, + { "oscr;", 0x02134 }, + { "oslash;", 0x000F8 }, + { "oslash", 0x000F8 }, + { "osol;", 0x02298 }, + { "otilde;", 0x000F5 }, + { "otilde", 0x000F5 }, + { "otimes;", 0x02297 }, + { "otimesas;", 0x02A36 }, + { "ouml;", 0x000F6 }, + { "ouml", 0x000F6 }, + { "ovbar;", 0x0233D }, + { "par;", 0x02225 }, + { "para;", 0x000B6 }, + { "para", 0x000B6 }, + { "parallel;", 0x02225 }, + { "parsim;", 0x02AF3 }, + { "parsl;", 0x02AFD }, + { "part;", 0x02202 }, + { "pcy;", 0x0043F }, + { "percnt;", 0x00025 }, + { "period;", 0x0002E }, + { "permil;", 0x02030 }, + { "perp;", 0x022A5 }, + { "pertenk;", 0x02031 }, + { "pfr;", 0x1D52D }, + { "phi;", 0x003C6 }, + { "phiv;", 0x003D5 }, + { "phmmat;", 0x02133 }, + { "phone;", 0x0260E }, + { "pi;", 0x003C0 }, + { "pitchfork;", 0x022D4 }, + { "piv;", 0x003D6 }, + { "planck;", 0x0210F }, + { "planckh;", 0x0210E }, + { "plankv;", 0x0210F }, + { "plus;", 0x0002B }, + { "plusacir;", 0x02A23 }, + { "plusb;", 0x0229E }, + { "pluscir;", 0x02A22 }, + { "plusdo;", 0x02214 }, + { "plusdu;", 0x02A25 }, + { "pluse;", 0x02A72 }, + { "plusmn;", 0x000B1 }, + { "plusmn", 0x000B1 }, + { "plussim;", 0x02A26 }, + { "plustwo;", 0x02A27 }, + { "pm;", 0x000B1 }, + { "pointint;", 0x02A15 }, + { "popf;", 0x1D561 }, + { "pound;", 0x000A3 }, + { "pound", 0x000A3 }, + { "pr;", 0x0227A }, + { "prE;", 0x02AB3 }, + { "prap;", 0x02AB7 }, + { "prcue;", 0x0227C }, + { "pre;", 0x02AAF }, + { "prec;", 0x0227A }, + { "precapprox;", 0x02AB7 }, + { "preccurlyeq;", 0x0227C }, + { "preceq;", 0x02AAF }, + { "precnapprox;", 0x02AB9 }, + { "precneqq;", 0x02AB5 }, + { "precnsim;", 0x022E8 }, + { "precsim;", 0x0227E }, + { "prime;", 0x02032 }, + { "primes;", 0x02119 }, + { "prnE;", 0x02AB5 }, + { "prnap;", 0x02AB9 }, + { "prnsim;", 0x022E8 }, + { "prod;", 0x0220F }, + { "profalar;", 0x0232E }, + { "profline;", 0x02312 }, + { "profsurf;", 0x02313 }, + { "prop;", 0x0221D }, + { "propto;", 0x0221D }, + { "prsim;", 0x0227E }, + { "prurel;", 0x022B0 }, + { "pscr;", 0x1D4C5 }, + { "psi;", 0x003C8 }, + { "puncsp;", 0x02008 }, + { "qfr;", 0x1D52E }, + { "qint;", 0x02A0C }, + { "qopf;", 0x1D562 }, + { "qprime;", 0x02057 }, + { "qscr;", 0x1D4C6 }, + { "quaternions;", 0x0210D }, + { "quatint;", 0x02A16 }, + { "quest;", 0x0003F }, + { "questeq;", 0x0225F }, + { "quot;", 0x00022 }, + { "quot", 0x00022 }, + { "rAarr;", 0x021DB }, + { "rArr;", 0x021D2 }, + { "rAtail;", 0x0291C }, + { "rBarr;", 0x0290F }, + { "rHar;", 0x02964 }, + { "racute;", 0x00155 }, + { "radic;", 0x0221A }, + { "raemptyv;", 0x029B3 }, + { "rang;", 0x027E9 }, + { "rangd;", 0x02992 }, + { "range;", 0x029A5 }, + { "rangle;", 0x027E9 }, + { "raquo;", 0x000BB }, + { "raquo", 0x000BB }, + { "rarr;", 0x02192 }, + { "rarrap;", 0x02975 }, + { "rarrb;", 0x021E5 }, + { "rarrbfs;", 0x02920 }, + { "rarrc;", 0x02933 }, + { "rarrfs;", 0x0291E }, + { "rarrhk;", 0x021AA }, + { "rarrlp;", 0x021AC }, + { "rarrpl;", 0x02945 }, + { "rarrsim;", 0x02974 }, + { "rarrtl;", 0x021A3 }, + { "rarrw;", 0x0219D }, + { "ratail;", 0x0291A }, + { "ratio;", 0x02236 }, + { "rationals;", 0x0211A }, + { "rbarr;", 0x0290D }, + { "rbbrk;", 0x02773 }, + { "rbrace;", 0x0007D }, + { "rbrack;", 0x0005D }, + { "rbrke;", 0x0298C }, + { "rbrksld;", 0x0298E }, + { "rbrkslu;", 0x02990 }, + { "rcaron;", 0x00159 }, + { "rcedil;", 0x00157 }, + { "rceil;", 0x02309 }, + { "rcub;", 0x0007D }, + { "rcy;", 0x00440 }, + { "rdca;", 0x02937 }, + { "rdldhar;", 0x02969 }, + { "rdquo;", 0x0201D }, + { "rdquor;", 0x0201D }, + { "rdsh;", 0x021B3 }, + { "real;", 0x0211C }, + { "realine;", 0x0211B }, + { "realpart;", 0x0211C }, + { "reals;", 0x0211D }, + { "rect;", 0x025AD }, + { "reg;", 0x000AE }, + { "reg", 0x000AE }, + { "rfisht;", 0x0297D }, + { "rfloor;", 0x0230B }, + { "rfr;", 0x1D52F }, + { "rhard;", 0x021C1 }, + { "rharu;", 0x021C0 }, + { "rharul;", 0x0296C }, + { "rho;", 0x003C1 }, + { "rhov;", 0x003F1 }, + { "rightarrow;", 0x02192 }, + { "rightarrowtail;", 0x021A3 }, + { "rightharpoondown;", 0x021C1 }, + { "rightharpoonup;", 0x021C0 }, + { "rightleftarrows;", 0x021C4 }, + { "rightleftharpoons;", 0x021CC }, + { "rightrightarrows;", 0x021C9 }, + { "rightsquigarrow;", 0x0219D }, + { "rightthreetimes;", 0x022CC }, + { "ring;", 0x002DA }, + { "risingdotseq;", 0x02253 }, + { "rlarr;", 0x021C4 }, + { "rlhar;", 0x021CC }, + { "rlm;", 0x0200F }, + { "rmoust;", 0x023B1 }, + { "rmoustache;", 0x023B1 }, + { "rnmid;", 0x02AEE }, + { "roang;", 0x027ED }, + { "roarr;", 0x021FE }, + { "robrk;", 0x027E7 }, + { "ropar;", 0x02986 }, + { "ropf;", 0x1D563 }, + { "roplus;", 0x02A2E }, + { "rotimes;", 0x02A35 }, + { "rpar;", 0x00029 }, + { "rpargt;", 0x02994 }, + { "rppolint;", 0x02A12 }, + { "rrarr;", 0x021C9 }, + { "rsaquo;", 0x0203A }, + { "rscr;", 0x1D4C7 }, + { "rsh;", 0x021B1 }, + { "rsqb;", 0x0005D }, + { "rsquo;", 0x02019 }, + { "rsquor;", 0x02019 }, + { "rthree;", 0x022CC }, + { "rtimes;", 0x022CA }, + { "rtri;", 0x025B9 }, + { "rtrie;", 0x022B5 }, + { "rtrif;", 0x025B8 }, + { "rtriltri;", 0x029CE }, + { "ruluhar;", 0x02968 }, + { "rx;", 0x0211E }, + { "sacute;", 0x0015B }, + { "sbquo;", 0x0201A }, + { "sc;", 0x0227B }, + { "scE;", 0x02AB4 }, + { "scap;", 0x02AB8 }, + { "scaron;", 0x00161 }, + { "sccue;", 0x0227D }, + { "sce;", 0x02AB0 }, + { "scedil;", 0x0015F }, + { "scirc;", 0x0015D }, + { "scnE;", 0x02AB6 }, + { "scnap;", 0x02ABA }, + { "scnsim;", 0x022E9 }, + { "scpolint;", 0x02A13 }, + { "scsim;", 0x0227F }, + { "scy;", 0x00441 }, + { "sdot;", 0x022C5 }, + { "sdotb;", 0x022A1 }, + { "sdote;", 0x02A66 }, + { "seArr;", 0x021D8 }, + { "searhk;", 0x02925 }, + { "searr;", 0x02198 }, + { "searrow;", 0x02198 }, + { "sect;", 0x000A7 }, + { "sect", 0x000A7 }, + { "semi;", 0x0003B }, + { "seswar;", 0x02929 }, + { "setminus;", 0x02216 }, + { "setmn;", 0x02216 }, + { "sext;", 0x02736 }, + { "sfr;", 0x1D530 }, + { "sfrown;", 0x02322 }, + { "sharp;", 0x0266F }, + { "shchcy;", 0x00449 }, + { "shcy;", 0x00448 }, + { "shortmid;", 0x02223 }, + { "shortparallel;", 0x02225 }, + { "shy;", 0x000AD }, + { "shy", 0x000AD }, + { "sigma;", 0x003C3 }, + { "sigmaf;", 0x003C2 }, + { "sigmav;", 0x003C2 }, + { "sim;", 0x0223C }, + { "simdot;", 0x02A6A }, + { "sime;", 0x02243 }, + { "simeq;", 0x02243 }, + { "simg;", 0x02A9E }, + { "simgE;", 0x02AA0 }, + { "siml;", 0x02A9D }, + { "simlE;", 0x02A9F }, + { "simne;", 0x02246 }, + { "simplus;", 0x02A24 }, + { "simrarr;", 0x02972 }, + { "slarr;", 0x02190 }, + { "smallsetminus;", 0x02216 }, + { "smashp;", 0x02A33 }, + { "smeparsl;", 0x029E4 }, + { "smid;", 0x02223 }, + { "smile;", 0x02323 }, + { "smt;", 0x02AAA }, + { "smte;", 0x02AAC }, + { "softcy;", 0x0044C }, + { "sol;", 0x0002F }, + { "solb;", 0x029C4 }, + { "solbar;", 0x0233F }, + { "sopf;", 0x1D564 }, + { "spades;", 0x02660 }, + { "spadesuit;", 0x02660 }, + { "spar;", 0x02225 }, + { "sqcap;", 0x02293 }, + { "sqcup;", 0x02294 }, + { "sqsub;", 0x0228F }, + { "sqsube;", 0x02291 }, + { "sqsubset;", 0x0228F }, + { "sqsubseteq;", 0x02291 }, + { "sqsup;", 0x02290 }, + { "sqsupe;", 0x02292 }, + { "sqsupset;", 0x02290 }, + { "sqsupseteq;", 0x02292 }, + { "squ;", 0x025A1 }, + { "square;", 0x025A1 }, + { "squarf;", 0x025AA }, + { "squf;", 0x025AA }, + { "srarr;", 0x02192 }, + { "sscr;", 0x1D4C8 }, + { "ssetmn;", 0x02216 }, + { "ssmile;", 0x02323 }, + { "sstarf;", 0x022C6 }, + { "star;", 0x02606 }, + { "starf;", 0x02605 }, + { "straightepsilon;", 0x003F5 }, + { "straightphi;", 0x003D5 }, + { "strns;", 0x000AF }, + { "sub;", 0x02282 }, + { "subE;", 0x02AC5 }, + { "subdot;", 0x02ABD }, + { "sube;", 0x02286 }, + { "subedot;", 0x02AC3 }, + { "submult;", 0x02AC1 }, + { "subnE;", 0x02ACB }, + { "subne;", 0x0228A }, + { "subplus;", 0x02ABF }, + { "subrarr;", 0x02979 }, + { "subset;", 0x02282 }, + { "subseteq;", 0x02286 }, + { "subseteqq;", 0x02AC5 }, + { "subsetneq;", 0x0228A }, + { "subsetneqq;", 0x02ACB }, + { "subsim;", 0x02AC7 }, + { "subsub;", 0x02AD5 }, + { "subsup;", 0x02AD3 }, + { "succ;", 0x0227B }, + { "succapprox;", 0x02AB8 }, + { "succcurlyeq;", 0x0227D }, + { "succeq;", 0x02AB0 }, + { "succnapprox;", 0x02ABA }, + { "succneqq;", 0x02AB6 }, + { "succnsim;", 0x022E9 }, + { "succsim;", 0x0227F }, + { "sum;", 0x02211 }, + { "sung;", 0x0266A }, + { "sup1;", 0x000B9 }, + { "sup1", 0x000B9 }, + { "sup2;", 0x000B2 }, + { "sup2", 0x000B2 }, + { "sup3;", 0x000B3 }, + { "sup3", 0x000B3 }, + { "sup;", 0x02283 }, + { "supE;", 0x02AC6 }, + { "supdot;", 0x02ABE }, + { "supdsub;", 0x02AD8 }, + { "supe;", 0x02287 }, + { "supedot;", 0x02AC4 }, + { "suphsol;", 0x027C9 }, + { "suphsub;", 0x02AD7 }, + { "suplarr;", 0x0297B }, + { "supmult;", 0x02AC2 }, + { "supnE;", 0x02ACC }, + { "supne;", 0x0228B }, + { "supplus;", 0x02AC0 }, + { "supset;", 0x02283 }, + { "supseteq;", 0x02287 }, + { "supseteqq;", 0x02AC6 }, + { "supsetneq;", 0x0228B }, + { "supsetneqq;", 0x02ACC }, + { "supsim;", 0x02AC8 }, + { "supsub;", 0x02AD4 }, + { "supsup;", 0x02AD6 }, + { "swArr;", 0x021D9 }, + { "swarhk;", 0x02926 }, + { "swarr;", 0x02199 }, + { "swarrow;", 0x02199 }, + { "swnwar;", 0x0292A }, + { "szlig;", 0x000DF }, + { "szlig", 0x000DF }, + { "target;", 0x02316 }, + { "tau;", 0x003C4 }, + { "tbrk;", 0x023B4 }, + { "tcaron;", 0x00165 }, + { "tcedil;", 0x00163 }, + { "tcy;", 0x00442 }, + { "tdot;", 0x020DB }, + { "telrec;", 0x02315 }, + { "tfr;", 0x1D531 }, + { "there4;", 0x02234 }, + { "therefore;", 0x02234 }, + { "theta;", 0x003B8 }, + { "thetasym;", 0x003D1 }, + { "thetav;", 0x003D1 }, + { "thickapprox;", 0x02248 }, + { "thicksim;", 0x0223C }, + { "thinsp;", 0x02009 }, + { "thkap;", 0x02248 }, + { "thksim;", 0x0223C }, + { "thorn;", 0x000FE }, + { "thorn", 0x000FE }, + { "tilde;", 0x002DC }, + { "times;", 0x000D7 }, + { "times", 0x000D7 }, + { "timesb;", 0x022A0 }, + { "timesbar;", 0x02A31 }, + { "timesd;", 0x02A30 }, + { "tint;", 0x0222D }, + { "toea;", 0x02928 }, + { "top;", 0x022A4 }, + { "topbot;", 0x02336 }, + { "topcir;", 0x02AF1 }, + { "topf;", 0x1D565 }, + { "topfork;", 0x02ADA }, + { "tosa;", 0x02929 }, + { "tprime;", 0x02034 }, + { "trade;", 0x02122 }, + { "triangle;", 0x025B5 }, + { "triangledown;", 0x025BF }, + { "triangleleft;", 0x025C3 }, + { "trianglelefteq;", 0x022B4 }, + { "triangleq;", 0x0225C }, + { "triangleright;", 0x025B9 }, + { "trianglerighteq;", 0x022B5 }, + { "tridot;", 0x025EC }, + { "trie;", 0x0225C }, + { "triminus;", 0x02A3A }, + { "triplus;", 0x02A39 }, + { "trisb;", 0x029CD }, + { "tritime;", 0x02A3B }, + { "trpezium;", 0x023E2 }, + { "tscr;", 0x1D4C9 }, + { "tscy;", 0x00446 }, + { "tshcy;", 0x0045B }, + { "tstrok;", 0x00167 }, + { "twixt;", 0x0226C }, + { "twoheadleftarrow;", 0x0219E }, + { "twoheadrightarrow;", 0x021A0 }, + { "uArr;", 0x021D1 }, + { "uHar;", 0x02963 }, + { "uacute;", 0x000FA }, + { "uacute", 0x000FA }, + { "uarr;", 0x02191 }, + { "ubrcy;", 0x0045E }, + { "ubreve;", 0x0016D }, + { "ucirc;", 0x000FB }, + { "ucirc", 0x000FB }, + { "ucy;", 0x00443 }, + { "udarr;", 0x021C5 }, + { "udblac;", 0x00171 }, + { "udhar;", 0x0296E }, + { "ufisht;", 0x0297E }, + { "ufr;", 0x1D532 }, + { "ugrave;", 0x000F9 }, + { "ugrave", 0x000F9 }, + { "uharl;", 0x021BF }, + { "uharr;", 0x021BE }, + { "uhblk;", 0x02580 }, + { "ulcorn;", 0x0231C }, + { "ulcorner;", 0x0231C }, + { "ulcrop;", 0x0230F }, + { "ultri;", 0x025F8 }, + { "umacr;", 0x0016B }, + { "uml;", 0x000A8 }, + { "uml", 0x000A8 }, + { "uogon;", 0x00173 }, + { "uopf;", 0x1D566 }, + { "uparrow;", 0x02191 }, + { "updownarrow;", 0x02195 }, + { "upharpoonleft;", 0x021BF }, + { "upharpoonright;", 0x021BE }, + { "uplus;", 0x0228E }, + { "upsi;", 0x003C5 }, + { "upsih;", 0x003D2 }, + { "upsilon;", 0x003C5 }, + { "upuparrows;", 0x021C8 }, + { "urcorn;", 0x0231D }, + { "urcorner;", 0x0231D }, + { "urcrop;", 0x0230E }, + { "uring;", 0x0016F }, + { "urtri;", 0x025F9 }, + { "uscr;", 0x1D4CA }, + { "utdot;", 0x022F0 }, + { "utilde;", 0x00169 }, + { "utri;", 0x025B5 }, + { "utrif;", 0x025B4 }, + { "uuarr;", 0x021C8 }, + { "uuml;", 0x000FC }, + { "uuml", 0x000FC }, + { "uwangle;", 0x029A7 }, + { "vArr;", 0x021D5 }, + { "vBar;", 0x02AE8 }, + { "vBarv;", 0x02AE9 }, + { "vDash;", 0x022A8 }, + { "vangrt;", 0x0299C }, + { "varepsilon;", 0x003F5 }, + { "varkappa;", 0x003F0 }, + { "varnothing;", 0x02205 }, + { "varphi;", 0x003D5 }, + { "varpi;", 0x003D6 }, + { "varpropto;", 0x0221D }, + { "varr;", 0x02195 }, + { "varrho;", 0x003F1 }, + { "varsigma;", 0x003C2 }, + { "vartheta;", 0x003D1 }, + { "vartriangleleft;", 0x022B2 }, + { "vartriangleright;", 0x022B3 }, + { "vcy;", 0x00432 }, + { "vdash;", 0x022A2 }, + { "vee;", 0x02228 }, + { "veebar;", 0x022BB }, + { "veeeq;", 0x0225A }, + { "vellip;", 0x022EE }, + { "verbar;", 0x0007C }, + { "vert;", 0x0007C }, + { "vfr;", 0x1D533 }, + { "vltri;", 0x022B2 }, + { "vopf;", 0x1D567 }, + { "vprop;", 0x0221D }, + { "vrtri;", 0x022B3 }, + { "vscr;", 0x1D4CB }, + { "vzigzag;", 0x0299A }, + { "wcirc;", 0x00175 }, + { "wedbar;", 0x02A5F }, + { "wedge;", 0x02227 }, + { "wedgeq;", 0x02259 }, + { "weierp;", 0x02118 }, + { "wfr;", 0x1D534 }, + { "wopf;", 0x1D568 }, + { "wp;", 0x02118 }, + { "wr;", 0x02240 }, + { "wreath;", 0x02240 }, + { "wscr;", 0x1D4CC }, + { "xcap;", 0x022C2 }, + { "xcirc;", 0x025EF }, + { "xcup;", 0x022C3 }, + { "xdtri;", 0x025BD }, + { "xfr;", 0x1D535 }, + { "xhArr;", 0x027FA }, + { "xharr;", 0x027F7 }, + { "xi;", 0x003BE }, + { "xlArr;", 0x027F8 }, + { "xlarr;", 0x027F5 }, + { "xmap;", 0x027FC }, + { "xnis;", 0x022FB }, + { "xodot;", 0x02A00 }, + { "xopf;", 0x1D569 }, + { "xoplus;", 0x02A01 }, + { "xotime;", 0x02A02 }, + { "xrArr;", 0x027F9 }, + { "xrarr;", 0x027F6 }, + { "xscr;", 0x1D4CD }, + { "xsqcup;", 0x02A06 }, + { "xuplus;", 0x02A04 }, + { "xutri;", 0x025B3 }, + { "xvee;", 0x022C1 }, + { "xwedge;", 0x022C0 }, + { "yacute;", 0x000FD }, + { "yacute", 0x000FD }, + { "yacy;", 0x0044F }, + { "ycirc;", 0x00177 }, + { "ycy;", 0x0044B }, + { "yen;", 0x000A5 }, + { "yen", 0x000A5 }, + { "yfr;", 0x1D536 }, + { "yicy;", 0x00457 }, + { "yopf;", 0x1D56A }, + { "yscr;", 0x1D4CE }, + { "yucy;", 0x0044E }, + { "yuml;", 0x000FF }, + { "yuml", 0x000FF }, + { "zacute;", 0x0017A }, + { "zcaron;", 0x0017E }, + { "zcy;", 0x00437 }, + { "zdot;", 0x0017C }, + { "zeetrf;", 0x02128 }, + { "zeta;", 0x003B6 }, + { "zfr;", 0x1D537 }, + { "zhcy;", 0x00436 }, + { "zigrarr;", 0x021DD }, + { "zopf;", 0x1D56B }, + { "zscr;", 0x1D4CF }, + { "zwj;", 0x0200D }, + { "zwnj;", 0x0200C } + }; + + constexpr struct { + StringView entity; + u32 code_point1; + u32 code_point2; + } double_code_point_entities[] = { + { "NotEqualTilde;", 0x02242, 0x00338 }, + { "NotGreaterFullEqual;", 0x02267, 0x00338 }, + { "NotGreaterGreater;", 0x0226B, 0x00338 }, + { "NotGreaterSlantEqual;", 0x02A7E, 0x00338 }, + { "NotHumpDownHump;", 0x0224E, 0x00338 }, + { "NotHumpEqual;", 0x0224F, 0x00338 }, + { "NotLeftTriangleBar;", 0x029CF, 0x00338 }, + { "NotLessLess;", 0x0226A, 0x00338 }, + { "NotLessSlantEqual;", 0x02A7D, 0x00338 }, + { "NotNestedGreaterGreater;", 0x02AA2, 0x00338 }, + { "NotNestedLessLess;", 0x02AA1, 0x00338 }, + { "NotPrecedesEqual;", 0x02AAF, 0x00338 }, + { "NotRightTriangleBar;", 0x029D0, 0x00338 }, + { "NotSquareSubset;", 0x0228F, 0x00338 }, + { "NotSquareSuperset;", 0x02290, 0x00338 }, + { "NotSubset;", 0x02282, 0x020D2 }, + { "NotSucceedsEqual;", 0x02AB0, 0x00338 }, + { "NotSucceedsTilde;", 0x0227F, 0x00338 }, + { "NotSuperset;", 0x02283, 0x020D2 }, + { "ThickSpace;", 0x0205F, 0x0200A }, + { "acE;", 0x0223E, 0x00333 }, + { "bne;", 0x0003D, 0x020E5 }, + { "bnequiv;", 0x02261, 0x020E5 }, + { "caps;", 0x02229, 0x0FE00 }, + { "cups;", 0x0222A, 0x0FE00 }, + { "fjlig;", 0x00066, 0x0006A }, + { "gesl;", 0x022DB, 0x0FE00 }, + { "gvertneqq;", 0x02269, 0x0FE00 }, + { "gvnE;", 0x02269, 0x0FE00 }, + { "lates;", 0x02AAD, 0x0FE00 }, + { "lesg;", 0x022DA, 0x0FE00 }, + { "lvertneqq;", 0x02268, 0x0FE00 }, + { "lvnE;", 0x02268, 0x0FE00 }, + { "nGg;", 0x022D9, 0x00338 }, + { "nGt;", 0x0226B, 0x020D2 }, + { "nGtv;", 0x0226B, 0x00338 }, + { "nLl;", 0x022D8, 0x00338 }, + { "nLt;", 0x0226A, 0x020D2 }, + { "nLtv;", 0x0226A, 0x00338 }, + { "nang;", 0x02220, 0x020D2 }, + { "napE;", 0x02A70, 0x00338 }, + { "napid;", 0x0224B, 0x00338 }, + { "nbump;", 0x0224E, 0x00338 }, + { "nbumpe;", 0x0224F, 0x00338 }, + { "ncongdot;", 0x02A6D, 0x00338 }, + { "nedot;", 0x02250, 0x00338 }, + { "nesim;", 0x02242, 0x00338 }, + { "ngE;", 0x02267, 0x00338 }, + { "ngeqq;", 0x02267, 0x00338 }, + { "ngeqslant;", 0x02A7E, 0x00338 }, + { "nges;", 0x02A7E, 0x00338 }, + { "nlE;", 0x02266, 0x00338 }, + { "nleqq;", 0x02266, 0x00338 }, + { "nleqslant;", 0x02A7D, 0x00338 }, + { "nles;", 0x02A7D, 0x00338 }, + { "notinE;", 0x022F9, 0x00338 }, + { "notindot;", 0x022F5, 0x00338 }, + { "nparsl;", 0x02AFD, 0x020E5 }, + { "npart;", 0x02202, 0x00338 }, + { "npre;", 0x02AAF, 0x00338 }, + { "npreceq;", 0x02AAF, 0x00338 }, + { "nrarrc;", 0x02933, 0x00338 }, + { "nrarrw;", 0x0219D, 0x00338 }, + { "nsce;", 0x02AB0, 0x00338 }, + { "nsubE;", 0x02AC5, 0x00338 }, + { "nsubset;", 0x02282, 0x020D2 }, + { "nsubseteqq;", 0x02AC5, 0x00338 }, + { "nsucceq;", 0x02AB0, 0x00338 }, + { "nsupE;", 0x02AC6, 0x00338 }, + { "nsupset;", 0x02283, 0x020D2 }, + { "nsupseteqq;", 0x02AC6, 0x00338 }, + { "nvap;", 0x0224D, 0x020D2 }, + { "nvge;", 0x02265, 0x020D2 }, + { "nvgt;", 0x0003E, 0x020D2 }, + { "nvle;", 0x02264, 0x020D2 }, + { "nvlt;", 0x0003C, 0x020D2 }, + { "nvltrie;", 0x022B4, 0x020D2 }, + { "nvrtrie;", 0x022B5, 0x020D2 }, + { "nvsim;", 0x0223C, 0x020D2 }, + { "race;", 0x0223D, 0x00331 }, + { "smtes;", 0x02AAC, 0x0FE00 }, + { "sqcaps;", 0x02293, 0x0FE00 }, + { "sqcups;", 0x02294, 0x0FE00 }, + { "varsubsetneq;", 0x0228A, 0x0FE00 }, + { "varsubsetneqq;", 0x02ACB, 0x0FE00 }, + { "varsupsetneq;", 0x0228B, 0x0FE00 }, + { "varsupsetneqq;", 0x02ACC, 0x0FE00 }, + { "vnsub;", 0x02282, 0x020D2 }, + { "vnsup;", 0x02283, 0x020D2 }, + { "vsubnE;", 0x02ACB, 0x0FE00 }, + { "vsubne;", 0x0228A, 0x0FE00 }, + { "vsupnE;", 0x02ACC, 0x0FE00 }, + { "vsupne;", 0x0228B, 0x0FE00 }, + }; + + EntityMatch match; + + for (auto& single_code_point_entity : single_code_point_entities) { + if (entity.starts_with(single_code_point_entity.entity)) { + if (match.entity.is_null() || single_code_point_entity.entity.length() > match.entity.length()) + match = { { single_code_point_entity.code_point }, single_code_point_entity.entity }; + } + } + + for (auto& double_code_point_entity : double_code_point_entities) { + if (entity.starts_with(double_code_point_entity.entity)) { + if (match.entity.is_null() || double_code_point_entity.entity.length() > match.entity.length()) + match = EntityMatch { { double_code_point_entity.code_point1, double_code_point_entity.code_point2 }, StringView(double_code_point_entity.entity) }; + } + } + + if (match.entity.is_empty()) + return {}; + return match; +} + +} +} diff --git a/Userland/Libraries/LibWeb/HTML/Parser/Entities.h b/Userland/Libraries/LibWeb/HTML/Parser/Entities.h new file mode 100644 index 0000000000..0701f3be8d --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Parser/Entities.h @@ -0,0 +1,43 @@ +/* + * 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 <AK/StringView.h> +#include <AK/Vector.h> + +namespace Web { +namespace HTML { + +struct EntityMatch { + Vector<u32, 2> code_points; + StringView entity; +}; + +Optional<EntityMatch> code_points_from_entity(const StringView&); + +} +} diff --git a/Userland/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.cpp b/Userland/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.cpp new file mode 100644 index 0000000000..446888aeb3 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.cpp @@ -0,0 +1,3027 @@ +/* + * 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. + */ + +//#define PARSER_DEBUG + +#include <AK/Utf32View.h> +#include <LibTextCodec/Decoder.h> +#include <LibWeb/DOM/Comment.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/DocumentType.h> +#include <LibWeb/DOM/ElementFactory.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/DOM/Text.h> +#include <LibWeb/DOM/Window.h> +#include <LibWeb/HTML/EventNames.h> +#include <LibWeb/HTML/HTMLFormElement.h> +#include <LibWeb/HTML/HTMLHeadElement.h> +#include <LibWeb/HTML/HTMLScriptElement.h> +#include <LibWeb/HTML/HTMLTemplateElement.h> +#include <LibWeb/HTML/Parser/HTMLDocumentParser.h> +#include <LibWeb/HTML/Parser/HTMLToken.h> +#include <LibWeb/Namespace.h> +#include <LibWeb/SVG/TagNames.h> + +namespace Web::HTML { + +#define PARSE_ERROR() \ + do { \ + dbg() << "Parse error! " << __PRETTY_FUNCTION__ << " @ " << __LINE__; \ + } while (0) + +static Vector<FlyString> s_quirks_public_ids = { + "+//Silmaril//dtd html Pro v0r11 19970101//", + "-//AS//DTD HTML 3.0 asWedit + extensions//", + "-//AdvaSoft Ltd//DTD HTML 3.0 asWedit + extensions//", + "-//IETF//DTD HTML 2.0 Level 1//", + "-//IETF//DTD HTML 2.0 Level 2//", + "-//IETF//DTD HTML 2.0 Strict Level 1//", + "-//IETF//DTD HTML 2.0 Strict Level 2//", + "-//IETF//DTD HTML 2.0 Strict//", + "-//IETF//DTD HTML 2.0//", + "-//IETF//DTD HTML 2.1E//", + "-//IETF//DTD HTML 3.0//", + "-//IETF//DTD HTML 3.2 Final//", + "-//IETF//DTD HTML 3.2//", + "-//IETF//DTD HTML 3//", + "-//IETF//DTD HTML Level 0//", + "-//IETF//DTD HTML Level 1//", + "-//IETF//DTD HTML Level 2//", + "-//IETF//DTD HTML Level 3//", + "-//IETF//DTD HTML Strict Level 0//", + "-//IETF//DTD HTML Strict Level 1//", + "-//IETF//DTD HTML Strict Level 2//", + "-//IETF//DTD HTML Strict Level 3//", + "-//IETF//DTD HTML Strict//", + "-//IETF//DTD HTML//", + "-//Metrius//DTD Metrius Presentational//", + "-//Microsoft//DTD Internet Explorer 2.0 HTML Strict//", + "-//Microsoft//DTD Internet Explorer 2.0 HTML//", + "-//Microsoft//DTD Internet Explorer 2.0 Tables//", + "-//Microsoft//DTD Internet Explorer 3.0 HTML Strict//", + "-//Microsoft//DTD Internet Explorer 3.0 HTML//", + "-//Microsoft//DTD Internet Explorer 3.0 Tables//", + "-//Netscape Comm. Corp.//DTD HTML//", + "-//Netscape Comm. Corp.//DTD Strict HTML//", + "-//O'Reilly and Associates//DTD HTML 2.0//", + "-//O'Reilly and Associates//DTD HTML Extended 1.0//", + "-//O'Reilly and Associates//DTD HTML Extended Relaxed 1.0//", + "-//SQ//DTD HTML 2.0 HoTMetaL + extensions//", + "-//SoftQuad Software//DTD HoTMetaL PRO 6.0::19990601::extensions to HTML 4.0//", + "-//SoftQuad//DTD HoTMetaL PRO 4.0::19971010::extensions to HTML 4.0//", + "-//Spyglass//DTD HTML 2.0 Extended//", + "-//Sun Microsystems Corp.//DTD HotJava HTML//", + "-//Sun Microsystems Corp.//DTD HotJava Strict HTML//", + "-//W3C//DTD HTML 3 1995-03-24//", + "-//W3C//DTD HTML 3.2 Draft//", + "-//W3C//DTD HTML 3.2 Final//", + "-//W3C//DTD HTML 3.2//", + "-//W3C//DTD HTML 3.2S Draft//", + "-//W3C//DTD HTML 4.0 Frameset//", + "-//W3C//DTD HTML 4.0 Transitional//", + "-//W3C//DTD HTML Experimental 19960712//", + "-//W3C//DTD HTML Experimental 970421//", + "-//W3C//DTD W3 HTML//", + "-//W3O//DTD W3 HTML 3.0//", + "-//WebTechs//DTD Mozilla HTML 2.0//", + "-//WebTechs//DTD Mozilla HTML//" +}; + +RefPtr<DOM::Document> parse_html_document(const StringView& data, const URL& url, const String& encoding) +{ + auto document = DOM::Document::create(url); + HTMLDocumentParser parser(document, data, encoding); + parser.run(url); + return document; +} + +HTMLDocumentParser::HTMLDocumentParser(DOM::Document& document, const StringView& input, const String& encoding) + : m_tokenizer(input, encoding) + , m_document(document) +{ + m_document->set_should_invalidate_styles_on_attribute_changes(false); + m_document->set_encoding(TextCodec::get_standardized_encoding(encoding)); +} + +HTMLDocumentParser::~HTMLDocumentParser() +{ + m_document->set_should_invalidate_styles_on_attribute_changes(true); +} + +void HTMLDocumentParser::run(const URL& url) +{ + m_document->set_url(url); + m_document->set_source(m_tokenizer.source()); + + for (;;) { + auto optional_token = m_tokenizer.next_token(); + if (!optional_token.has_value()) + break; + auto& token = optional_token.value(); + +#ifdef PARSER_DEBUG + dbg() << "[" << insertion_mode_name() << "] " << token.to_string(); +#endif + // FIXME: If the adjusted current node is a MathML text integration point and the token is a start tag whose tag name is neither "mglyph" nor "malignmark" + // FIXME: If the adjusted current node is a MathML text integration point and the token is a character token + // FIXME: If the adjusted current node is a MathML annotation-xml element and the token is a start tag whose tag name is "svg" + // FIXME: If the adjusted current node is an HTML integration point and the token is a start tag + // FIXME: If the adjusted current node is an HTML integration point and the token is a character token + if (m_stack_of_open_elements.is_empty() + || adjusted_current_node().namespace_() == Namespace::HTML + || token.is_end_of_file()) { + process_using_the_rules_for(m_insertion_mode, token); + } else { + process_using_the_rules_for_foreign_content(token); + } + + if (m_stop_parsing) { +#ifdef PARSER_DEBUG + dbg() << "Stop parsing" << (m_parsing_fragment ? " fragment" : "") << "! :^)"; +#endif + break; + } + } + + flush_character_insertions(); + + // "The end" + + m_document->set_ready_state("interactive"); + + auto scripts_to_execute_when_parsing_has_finished = m_document->take_scripts_to_execute_when_parsing_has_finished({}); + for (auto& script : scripts_to_execute_when_parsing_has_finished) { + script.execute_script(); + } + + auto content_loaded_event = DOM::Event::create(HTML::EventNames::DOMContentLoaded); + content_loaded_event->set_bubbles(true); + m_document->dispatch_event(content_loaded_event); + + auto scripts_to_execute_as_soon_as_possible = m_document->take_scripts_to_execute_as_soon_as_possible({}); + for (auto& script : scripts_to_execute_as_soon_as_possible) { + script.execute_script(); + } + + // FIXME: Spin the event loop until there is nothing that delays the load event in the Document. + + m_document->set_ready_state("complete"); + m_document->window().dispatch_event(DOM::Event::create(HTML::EventNames::load)); + + m_document->set_ready_for_post_load_tasks(true); + m_document->completely_finish_loading(); +} + +void HTMLDocumentParser::process_using_the_rules_for(InsertionMode mode, HTMLToken& token) +{ + switch (mode) { + case InsertionMode::Initial: + handle_initial(token); + break; + case InsertionMode::BeforeHTML: + handle_before_html(token); + break; + case InsertionMode::BeforeHead: + handle_before_head(token); + break; + case InsertionMode::InHead: + handle_in_head(token); + break; + case InsertionMode::InHeadNoscript: + handle_in_head_noscript(token); + break; + case InsertionMode::AfterHead: + handle_after_head(token); + break; + case InsertionMode::InBody: + handle_in_body(token); + break; + case InsertionMode::AfterBody: + handle_after_body(token); + break; + case InsertionMode::AfterAfterBody: + handle_after_after_body(token); + break; + case InsertionMode::Text: + handle_text(token); + break; + case InsertionMode::InTable: + handle_in_table(token); + break; + case InsertionMode::InTableBody: + handle_in_table_body(token); + break; + case InsertionMode::InRow: + handle_in_row(token); + break; + case InsertionMode::InCell: + handle_in_cell(token); + break; + case InsertionMode::InTableText: + handle_in_table_text(token); + break; + case InsertionMode::InSelectInTable: + handle_in_select_in_table(token); + break; + case InsertionMode::InSelect: + handle_in_select(token); + break; + case InsertionMode::InCaption: + handle_in_caption(token); + break; + case InsertionMode::InColumnGroup: + handle_in_column_group(token); + break; + case InsertionMode::InTemplate: + handle_in_template(token); + break; + case InsertionMode::InFrameset: + handle_in_frameset(token); + break; + case InsertionMode::AfterFrameset: + handle_after_frameset(token); + break; + case InsertionMode::AfterAfterFrameset: + handle_after_after_frameset(token); + break; + default: + ASSERT_NOT_REACHED(); + } +} + +DOM::QuirksMode HTMLDocumentParser::which_quirks_mode(const HTMLToken& doctype_token) const +{ + if (doctype_token.m_doctype.force_quirks) + return DOM::QuirksMode::Yes; + + // NOTE: The tokenizer puts the name into lower case for us. + if (doctype_token.m_doctype.name.to_string() != "html") + return DOM::QuirksMode::Yes; + + auto public_identifier = doctype_token.m_doctype.public_identifier.to_string(); + auto system_identifier = doctype_token.m_doctype.system_identifier.to_string(); + + if (public_identifier.equals_ignoring_case("-//W3O//DTD W3 HTML Strict 3.0//EN//")) + return DOM::QuirksMode::Yes; + + if (public_identifier.equals_ignoring_case("-/W3C/DTD HTML 4.0 Transitional/EN")) + return DOM::QuirksMode::Yes; + + if (public_identifier.equals_ignoring_case("HTML")) + return DOM::QuirksMode::Yes; + + if (system_identifier.equals_ignoring_case("http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd")) + return DOM::QuirksMode::Yes; + + for (auto& public_id : s_quirks_public_ids) { + if (public_identifier.starts_with(public_id, CaseSensitivity::CaseInsensitive)) + return DOM::QuirksMode::Yes; + } + + if (doctype_token.m_doctype.missing_system_identifier) { + if (public_identifier.starts_with("-//W3C//DTD HTML 4.01 Frameset//", CaseSensitivity::CaseInsensitive)) + return DOM::QuirksMode::Yes; + + if (public_identifier.starts_with("-//W3C//DTD HTML 4.01 Transitional//", CaseSensitivity::CaseInsensitive)) + return DOM::QuirksMode::Yes; + } + + if (public_identifier.starts_with("-//W3C//DTD XHTML 1.0 Frameset//", CaseSensitivity::CaseInsensitive)) + return DOM::QuirksMode::Limited; + + if (public_identifier.starts_with("-//W3C//DTD XHTML 1.0 Transitional//", CaseSensitivity::CaseInsensitive)) + return DOM::QuirksMode::Limited; + + if (!doctype_token.m_doctype.missing_system_identifier) { + if (public_identifier.starts_with("-//W3C//DTD HTML 4.01 Frameset//", CaseSensitivity::CaseInsensitive)) + return DOM::QuirksMode::Limited; + + if (public_identifier.starts_with("-//W3C//DTD HTML 4.01 Transitional//", CaseSensitivity::CaseInsensitive)) + return DOM::QuirksMode::Limited; + } + + return DOM::QuirksMode::No; +} + +void HTMLDocumentParser::handle_initial(HTMLToken& token) +{ + if (token.is_character() && token.is_parser_whitespace()) { + return; + } + + if (token.is_comment()) { + auto comment = adopt(*new DOM::Comment(document(), token.m_comment_or_character.data.to_string())); + document().append_child(move(comment)); + return; + } + + if (token.is_doctype()) { + auto doctype = adopt(*new DOM::DocumentType(document())); + doctype->set_name(token.m_doctype.name.to_string()); + doctype->set_public_id(token.m_doctype.public_identifier.to_string()); + doctype->set_system_id(token.m_doctype.system_identifier.to_string()); + document().append_child(move(doctype)); + document().set_quirks_mode(which_quirks_mode(token)); + m_insertion_mode = InsertionMode::BeforeHTML; + return; + } + + PARSE_ERROR(); + document().set_quirks_mode(DOM::QuirksMode::Yes); + m_insertion_mode = InsertionMode::BeforeHTML; + process_using_the_rules_for(InsertionMode::BeforeHTML, token); +} + +void HTMLDocumentParser::handle_before_html(HTMLToken& token) +{ + if (token.is_doctype()) { + PARSE_ERROR(); + return; + } + + if (token.is_comment()) { + auto comment = adopt(*new DOM::Comment(document(), token.m_comment_or_character.data.to_string())); + document().append_child(move(comment)); + return; + } + + if (token.is_character() && token.is_parser_whitespace()) { + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) { + auto element = create_element_for(token, Namespace::HTML); + document().append_child(element); + m_stack_of_open_elements.push(move(element)); + m_insertion_mode = InsertionMode::BeforeHead; + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::head, HTML::TagNames::body, HTML::TagNames::html, HTML::TagNames::br)) { + goto AnythingElse; + } + + if (token.is_end_tag()) { + PARSE_ERROR(); + return; + } + +AnythingElse: + auto element = create_element(document(), HTML::TagNames::html, Namespace::HTML); + document().append_child(element); + m_stack_of_open_elements.push(element); + // FIXME: If the Document is being loaded as part of navigation of a browsing context, then: run the application cache selection algorithm with no manifest, passing it the Document object. + m_insertion_mode = InsertionMode::BeforeHead; + process_using_the_rules_for(InsertionMode::BeforeHead, token); + return; +} + +DOM::Element& HTMLDocumentParser::current_node() +{ + return m_stack_of_open_elements.current_node(); +} + +DOM::Element& HTMLDocumentParser::adjusted_current_node() +{ + if (m_parsing_fragment && m_stack_of_open_elements.elements().size() == 1) + return *m_context_element; + + return current_node(); +} + +DOM::Element& HTMLDocumentParser::node_before_current_node() +{ + return m_stack_of_open_elements.elements().at(m_stack_of_open_elements.elements().size() - 2); +} + +HTMLDocumentParser::AdjustedInsertionLocation HTMLDocumentParser::find_appropriate_place_for_inserting_node() +{ + auto& target = current_node(); + HTMLDocumentParser::AdjustedInsertionLocation adjusted_insertion_location; + + if (m_foster_parenting && target.local_name().is_one_of(HTML::TagNames::table, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::tr)) { + auto last_template = m_stack_of_open_elements.last_element_with_tag_name(HTML::TagNames::template_); + auto last_table = m_stack_of_open_elements.last_element_with_tag_name(HTML::TagNames::table); + if (last_template.element && (!last_table.element || last_template.index > last_table.index)) { + // This returns the template content, so no need to check the parent is a template. + return { downcast<HTMLTemplateElement>(last_template.element)->content(), nullptr }; + } + if (!last_table.element) { + ASSERT(m_parsing_fragment); + // Guaranteed not to be a template element (it will be the html element), + // so no need to check the parent is a template. + return { m_stack_of_open_elements.elements().first(), nullptr }; + } + if (last_table.element->parent_node()) + adjusted_insertion_location = { last_table.element->parent_node(), last_table.element }; + else + adjusted_insertion_location = { m_stack_of_open_elements.element_before(*last_table.element), nullptr }; + } else { + adjusted_insertion_location = { target, nullptr }; + } + + if (is<HTMLTemplateElement>(*adjusted_insertion_location.parent)) + return { downcast<HTMLTemplateElement>(*adjusted_insertion_location.parent).content(), nullptr }; + + return adjusted_insertion_location; +} + +NonnullRefPtr<DOM::Element> HTMLDocumentParser::create_element_for(const HTMLToken& token, const FlyString& namespace_) +{ + auto element = create_element(document(), token.tag_name(), namespace_); + for (auto& attribute : token.m_tag.attributes) { + element->set_attribute(attribute.local_name_builder.to_string(), attribute.value_builder.to_string()); + } + return element; +} + +RefPtr<DOM::Element> HTMLDocumentParser::insert_foreign_element(const HTMLToken& token, const FlyString& namespace_) +{ + auto adjusted_insertion_location = find_appropriate_place_for_inserting_node(); + auto element = create_element_for(token, namespace_); + // FIXME: Check if it's possible to insert `element` at `adjusted_insertion_location` + adjusted_insertion_location.parent->insert_before(element, adjusted_insertion_location.insert_before_sibling); + m_stack_of_open_elements.push(element); + return element; +} + +RefPtr<DOM::Element> HTMLDocumentParser::insert_html_element(const HTMLToken& token) +{ + return insert_foreign_element(token, Namespace::HTML); +} + +void HTMLDocumentParser::handle_before_head(HTMLToken& token) +{ + if (token.is_character() && token.is_parser_whitespace()) { + return; + } + + if (token.is_comment()) { + insert_comment(token); + return; + } + + if (token.is_doctype()) { + PARSE_ERROR(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::head) { + auto element = insert_html_element(token); + m_head_element = downcast<HTMLHeadElement>(*element); + m_insertion_mode = InsertionMode::InHead; + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::head, HTML::TagNames::body, HTML::TagNames::html, HTML::TagNames::br)) { + goto AnythingElse; + } + + if (token.is_end_tag()) { + PARSE_ERROR(); + return; + } + +AnythingElse: + m_head_element = downcast<HTMLHeadElement>(*insert_html_element(HTMLToken::make_start_tag(HTML::TagNames::head))); + m_insertion_mode = InsertionMode::InHead; + process_using_the_rules_for(InsertionMode::InHead, token); + return; +} + +void HTMLDocumentParser::insert_comment(HTMLToken& token) +{ + auto data = token.m_comment_or_character.data.to_string(); + auto adjusted_insertion_location = find_appropriate_place_for_inserting_node(); + adjusted_insertion_location.parent->insert_before(adopt(*new DOM::Comment(document(), data)), adjusted_insertion_location.insert_before_sibling); +} + +void HTMLDocumentParser::handle_in_head(HTMLToken& token) +{ + if (token.is_parser_whitespace()) { + insert_character(token.code_point()); + return; + } + + if (token.is_comment()) { + insert_comment(token); + return; + } + + if (token.is_doctype()) { + PARSE_ERROR(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::base, HTML::TagNames::basefont, HTML::TagNames::bgsound, HTML::TagNames::link)) { + insert_html_element(token); + m_stack_of_open_elements.pop(); + token.acknowledge_self_closing_flag_if_set(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::meta) { + auto element = insert_html_element(token); + m_stack_of_open_elements.pop(); + token.acknowledge_self_closing_flag_if_set(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::title) { + insert_html_element(token); + m_tokenizer.switch_to({}, HTMLTokenizer::State::RCDATA); + m_original_insertion_mode = m_insertion_mode; + m_insertion_mode = InsertionMode::Text; + return; + } + + if (token.is_start_tag() && ((token.tag_name() == HTML::TagNames::noscript && m_scripting_enabled) || token.tag_name() == HTML::TagNames::noframes || token.tag_name() == HTML::TagNames::style)) { + parse_generic_raw_text_element(token); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::noscript && !m_scripting_enabled) { + insert_html_element(token); + m_insertion_mode = InsertionMode::InHeadNoscript; + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::script) { + auto adjusted_insertion_location = find_appropriate_place_for_inserting_node(); + auto element = create_element_for(token, Namespace::HTML); + auto& script_element = downcast<HTMLScriptElement>(*element); + script_element.set_parser_document({}, document()); + script_element.set_non_blocking({}, false); + + if (m_parsing_fragment) { + TODO(); + } + + if (m_invoked_via_document_write) { + TODO(); + } + + adjusted_insertion_location.parent->insert_before(element, adjusted_insertion_location.insert_before_sibling, false); + m_stack_of_open_elements.push(element); + m_tokenizer.switch_to({}, HTMLTokenizer::State::ScriptData); + m_original_insertion_mode = m_insertion_mode; + m_insertion_mode = InsertionMode::Text; + return; + } + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::head) { + m_stack_of_open_elements.pop(); + m_insertion_mode = InsertionMode::AfterHead; + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::body, HTML::TagNames::html, HTML::TagNames::br)) { + goto AnythingElse; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::template_) { + insert_html_element(token); + m_list_of_active_formatting_elements.add_marker(); + m_frameset_ok = false; + m_insertion_mode = InsertionMode::InTemplate; + m_stack_of_template_insertion_modes.append(InsertionMode::InTemplate); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::template_) { + if (!m_stack_of_open_elements.contains(HTML::TagNames::template_)) { + PARSE_ERROR(); + return; + } + + generate_all_implied_end_tags_thoroughly(); + + if (current_node().local_name() != HTML::TagNames::template_) + PARSE_ERROR(); + + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::template_); + m_list_of_active_formatting_elements.clear_up_to_the_last_marker(); + m_stack_of_template_insertion_modes.take_last(); + reset_the_insertion_mode_appropriately(); + return; + } + + if ((token.is_start_tag() && token.tag_name() == HTML::TagNames::head) || token.is_end_tag()) { + PARSE_ERROR(); + return; + } + +AnythingElse: + m_stack_of_open_elements.pop(); + m_insertion_mode = InsertionMode::AfterHead; + process_using_the_rules_for(m_insertion_mode, token); +} + +void HTMLDocumentParser::handle_in_head_noscript(HTMLToken& token) +{ + if (token.is_doctype()) { + PARSE_ERROR(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::noscript) { + m_stack_of_open_elements.pop(); + m_insertion_mode = InsertionMode::InHead; + return; + } + + if (token.is_parser_whitespace() || token.is_comment() || (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::basefont, HTML::TagNames::bgsound, HTML::TagNames::link, HTML::TagNames::meta, HTML::TagNames::noframes, HTML::TagNames::style))) { + process_using_the_rules_for(InsertionMode::InHead, token); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::br) { + goto AnythingElse; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::head, HTML::TagNames::noscript)) { + PARSE_ERROR(); + return; + } + +AnythingElse: + PARSE_ERROR(); + m_stack_of_open_elements.pop(); + m_insertion_mode = InsertionMode::InHead; + process_using_the_rules_for(m_insertion_mode, token); +} + +void HTMLDocumentParser::parse_generic_raw_text_element(HTMLToken& token) +{ + insert_html_element(token); + m_tokenizer.switch_to({}, HTMLTokenizer::State::RAWTEXT); + m_original_insertion_mode = m_insertion_mode; + m_insertion_mode = InsertionMode::Text; +} + +DOM::Text* HTMLDocumentParser::find_character_insertion_node() +{ + auto adjusted_insertion_location = find_appropriate_place_for_inserting_node(); + if (adjusted_insertion_location.insert_before_sibling) { + TODO(); + } + if (adjusted_insertion_location.parent->is_document()) + return nullptr; + if (adjusted_insertion_location.parent->last_child() && adjusted_insertion_location.parent->last_child()->is_text()) + return downcast<DOM::Text>(adjusted_insertion_location.parent->last_child()); + auto new_text_node = adopt(*new DOM::Text(document(), "")); + adjusted_insertion_location.parent->append_child(new_text_node); + return new_text_node; +} + +void HTMLDocumentParser::flush_character_insertions() +{ + if (m_character_insertion_builder.is_empty()) + return; + m_character_insertion_node->set_data(m_character_insertion_builder.to_string()); + m_character_insertion_node->parent()->children_changed(); + m_character_insertion_builder.clear(); +} + +void HTMLDocumentParser::insert_character(u32 data) +{ + auto node = find_character_insertion_node(); + if (node == m_character_insertion_node) { + m_character_insertion_builder.append(Utf32View { &data, 1 }); + return; + } + if (!m_character_insertion_node) { + m_character_insertion_node = node; + m_character_insertion_builder.append(Utf32View { &data, 1 }); + return; + } + flush_character_insertions(); + m_character_insertion_node = node; + m_character_insertion_builder.append(Utf32View { &data, 1 }); +} + +void HTMLDocumentParser::handle_after_head(HTMLToken& token) +{ + if (token.is_character() && token.is_parser_whitespace()) { + insert_character(token.code_point()); + return; + } + + if (token.is_comment()) { + insert_comment(token); + return; + } + + if (token.is_doctype()) { + PARSE_ERROR(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::body) { + insert_html_element(token); + m_frameset_ok = false; + m_insertion_mode = InsertionMode::InBody; + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::frameset) { + insert_html_element(token); + m_insertion_mode = InsertionMode::InFrameset; + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::base, HTML::TagNames::basefont, HTML::TagNames::bgsound, HTML::TagNames::link, HTML::TagNames::meta, HTML::TagNames::noframes, HTML::TagNames::script, HTML::TagNames::style, HTML::TagNames::template_, HTML::TagNames::title)) { + PARSE_ERROR(); + m_stack_of_open_elements.push(*m_head_element); + process_using_the_rules_for(InsertionMode::InHead, token); + m_stack_of_open_elements.elements().remove_first_matching([&](auto& entry) { + return entry.ptr() == m_head_element; + }); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::template_) { + process_using_the_rules_for(InsertionMode::InHead, token); + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::body, HTML::TagNames::html, HTML::TagNames::br)) { + goto AnythingElse; + } + + if ((token.is_start_tag() && token.tag_name() == HTML::TagNames::head) || token.is_end_tag()) { + PARSE_ERROR(); + return; + } + +AnythingElse: + insert_html_element(HTMLToken::make_start_tag(HTML::TagNames::body)); + m_insertion_mode = InsertionMode::InBody; + process_using_the_rules_for(m_insertion_mode, token); +} + +void HTMLDocumentParser::generate_implied_end_tags(const FlyString& exception) +{ + while (current_node().local_name() != exception && current_node().local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc)) + m_stack_of_open_elements.pop(); +} + +void HTMLDocumentParser::generate_all_implied_end_tags_thoroughly() +{ + while (current_node().local_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::colgroup, HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr)) + m_stack_of_open_elements.pop(); +} + +void HTMLDocumentParser::close_a_p_element() +{ + generate_implied_end_tags(HTML::TagNames::p); + if (current_node().local_name() != HTML::TagNames::p) { + PARSE_ERROR(); + } + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::p); +} + +void HTMLDocumentParser::handle_after_body(HTMLToken& token) +{ + if (token.is_character() && token.is_parser_whitespace()) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + + if (token.is_comment()) { + auto data = token.m_comment_or_character.data.to_string(); + auto& insertion_location = m_stack_of_open_elements.first(); + insertion_location.append_child(adopt(*new DOM::Comment(document(), data))); + return; + } + + if (token.is_doctype()) { + PARSE_ERROR(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::html) { + if (m_parsing_fragment) { + PARSE_ERROR(); + return; + } + m_insertion_mode = InsertionMode::AfterAfterBody; + return; + } + + if (token.is_end_of_file()) { + stop_parsing(); + return; + } + + PARSE_ERROR(); + m_insertion_mode = InsertionMode::InBody; + process_using_the_rules_for(InsertionMode::InBody, token); +} + +void HTMLDocumentParser::handle_after_after_body(HTMLToken& token) +{ + if (token.is_comment()) { + auto comment = adopt(*new DOM::Comment(document(), token.m_comment_or_character.data.to_string())); + document().append_child(move(comment)); + return; + } + + if (token.is_doctype() || token.is_parser_whitespace() || (token.is_start_tag() && token.tag_name() == HTML::TagNames::html)) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + + if (token.is_end_of_file()) { + stop_parsing(); + return; + } + + PARSE_ERROR(); + m_insertion_mode = InsertionMode::InBody; + process_using_the_rules_for(m_insertion_mode, token); +} + +void HTMLDocumentParser::reconstruct_the_active_formatting_elements() +{ + // FIXME: This needs to care about "markers" + + if (m_list_of_active_formatting_elements.is_empty()) + return; + + if (m_list_of_active_formatting_elements.entries().last().is_marker()) + return; + + if (m_stack_of_open_elements.contains(*m_list_of_active_formatting_elements.entries().last().element)) + return; + + ssize_t index = m_list_of_active_formatting_elements.entries().size() - 1; + RefPtr<DOM::Element> entry = m_list_of_active_formatting_elements.entries().at(index).element; + ASSERT(entry); + +Rewind: + if (index == 0) { + goto Create; + } + + --index; + entry = m_list_of_active_formatting_elements.entries().at(index).element; + ASSERT(entry); + + if (!m_stack_of_open_elements.contains(*entry)) + goto Rewind; + +Advance: + ++index; + entry = m_list_of_active_formatting_elements.entries().at(index).element; + ASSERT(entry); + +Create: + // FIXME: Hold on to the real token! + auto new_element = insert_html_element(HTMLToken::make_start_tag(entry->local_name())); + + m_list_of_active_formatting_elements.entries().at(index).element = *new_element; + + if (index != (ssize_t)m_list_of_active_formatting_elements.entries().size() - 1) + goto Advance; +} + +HTMLDocumentParser::AdoptionAgencyAlgorithmOutcome HTMLDocumentParser::run_the_adoption_agency_algorithm(HTMLToken& token) +{ + auto subject = token.tag_name(); + + // If the current node is an HTML element whose tag name is subject, + // and the current node is not in the list of active formatting elements, + // then pop the current node off the stack of open elements, and return. + if (current_node().local_name() == subject && !m_list_of_active_formatting_elements.contains(current_node())) { + m_stack_of_open_elements.pop(); + return AdoptionAgencyAlgorithmOutcome::DoNothing; + } + + size_t outer_loop_counter = 0; + + //OuterLoop: + if (outer_loop_counter >= 8) + return AdoptionAgencyAlgorithmOutcome::DoNothing; + + ++outer_loop_counter; + + auto formatting_element = m_list_of_active_formatting_elements.last_element_with_tag_name_before_marker(subject); + if (!formatting_element) + return AdoptionAgencyAlgorithmOutcome::RunAnyOtherEndTagSteps; + + if (!m_stack_of_open_elements.contains(*formatting_element)) { + PARSE_ERROR(); + // FIXME: If formatting element is not in the stack of open elements, + // then this is a parse error; remove the element from the list, and return. + TODO(); + } + + if (!m_stack_of_open_elements.has_in_scope(*formatting_element)) { + PARSE_ERROR(); + return AdoptionAgencyAlgorithmOutcome::DoNothing; + } + + if (formatting_element != ¤t_node()) { + PARSE_ERROR(); + } + + RefPtr<DOM::Element> furthest_block = m_stack_of_open_elements.topmost_special_node_below(*formatting_element); + + if (!furthest_block) { + while (¤t_node() != formatting_element) + m_stack_of_open_elements.pop(); + m_stack_of_open_elements.pop(); + + m_list_of_active_formatting_elements.remove(*formatting_element); + return AdoptionAgencyAlgorithmOutcome::DoNothing; + } + + // FIXME: Implement the rest of the AAA :^) + + TODO(); +} + +bool HTMLDocumentParser::is_special_tag(const FlyString& tag_name, const FlyString& namespace_) +{ + if (namespace_ == Namespace::HTML) { + return tag_name.is_one_of( + HTML::TagNames::address, + HTML::TagNames::applet, + HTML::TagNames::area, + HTML::TagNames::article, + HTML::TagNames::aside, + HTML::TagNames::base, + HTML::TagNames::basefont, + HTML::TagNames::bgsound, + HTML::TagNames::blockquote, + HTML::TagNames::body, + HTML::TagNames::br, + HTML::TagNames::button, + HTML::TagNames::caption, + HTML::TagNames::center, + HTML::TagNames::col, + HTML::TagNames::colgroup, + HTML::TagNames::dd, + HTML::TagNames::details, + HTML::TagNames::dir, + HTML::TagNames::div, + HTML::TagNames::dl, + HTML::TagNames::dt, + HTML::TagNames::embed, + HTML::TagNames::fieldset, + HTML::TagNames::figcaption, + HTML::TagNames::figure, + HTML::TagNames::footer, + HTML::TagNames::form, + HTML::TagNames::frame, + HTML::TagNames::frameset, + HTML::TagNames::h1, + HTML::TagNames::h2, + HTML::TagNames::h3, + HTML::TagNames::h4, + HTML::TagNames::h5, + HTML::TagNames::h6, + HTML::TagNames::head, + HTML::TagNames::header, + HTML::TagNames::hgroup, + HTML::TagNames::hr, + HTML::TagNames::html, + HTML::TagNames::iframe, + HTML::TagNames::img, + HTML::TagNames::input, + HTML::TagNames::keygen, + HTML::TagNames::li, + HTML::TagNames::link, + HTML::TagNames::listing, + HTML::TagNames::main, + HTML::TagNames::marquee, + HTML::TagNames::menu, + HTML::TagNames::meta, + HTML::TagNames::nav, + HTML::TagNames::noembed, + HTML::TagNames::noframes, + HTML::TagNames::noscript, + HTML::TagNames::object, + HTML::TagNames::ol, + HTML::TagNames::p, + HTML::TagNames::param, + HTML::TagNames::plaintext, + HTML::TagNames::pre, + HTML::TagNames::script, + HTML::TagNames::section, + HTML::TagNames::select, + HTML::TagNames::source, + HTML::TagNames::style, + HTML::TagNames::summary, + HTML::TagNames::table, + HTML::TagNames::tbody, + HTML::TagNames::td, + HTML::TagNames::template_, + HTML::TagNames::textarea, + HTML::TagNames::tfoot, + HTML::TagNames::th, + HTML::TagNames::thead, + HTML::TagNames::title, + HTML::TagNames::tr, + HTML::TagNames::track, + HTML::TagNames::ul, + HTML::TagNames::wbr, + HTML::TagNames::xmp); + } else if (namespace_ == Namespace::SVG) { + return tag_name.is_one_of( + SVG::TagNames::desc, + SVG::TagNames::foreignObject, + SVG::TagNames::title); + } else if (namespace_ == Namespace::MathML) { + TODO(); + } + + return false; +} + +void HTMLDocumentParser::handle_in_body(HTMLToken& token) +{ + if (token.is_character()) { + if (token.code_point() == 0) { + PARSE_ERROR(); + return; + } + if (token.is_parser_whitespace()) { + reconstruct_the_active_formatting_elements(); + insert_character(token.code_point()); + return; + } + reconstruct_the_active_formatting_elements(); + insert_character(token.code_point()); + m_frameset_ok = false; + return; + } + + if (token.is_comment()) { + insert_comment(token); + return; + } + + if (token.is_doctype()) { + PARSE_ERROR(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) { + PARSE_ERROR(); + if (m_stack_of_open_elements.contains(HTML::TagNames::template_)) + return; + for (auto& attribute : token.m_tag.attributes) { + if (current_node().has_attribute(attribute.local_name_builder.string_view())) + continue; + current_node().set_attribute(attribute.local_name_builder.to_string(), attribute.value_builder.to_string()); + } + return; + } + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::base, HTML::TagNames::basefont, HTML::TagNames::bgsound, HTML::TagNames::link, HTML::TagNames::meta, HTML::TagNames::noframes, HTML::TagNames::script, HTML::TagNames::style, HTML::TagNames::template_, HTML::TagNames::title)) { + process_using_the_rules_for(InsertionMode::InHead, token); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::template_) { + process_using_the_rules_for(InsertionMode::InHead, token); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::body) { + PARSE_ERROR(); + if (m_stack_of_open_elements.elements().size() == 1 + || m_stack_of_open_elements.elements().at(1).local_name() != HTML::TagNames::body + || m_stack_of_open_elements.contains(HTML::TagNames::template_)) { + ASSERT(m_parsing_fragment); + return; + } + m_frameset_ok = false; + auto& body_element = m_stack_of_open_elements.elements().at(1); + for (auto& attribute : token.m_tag.attributes) { + if (body_element.has_attribute(attribute.local_name_builder.string_view())) + continue; + body_element.set_attribute(attribute.local_name_builder.to_string(), attribute.value_builder.to_string()); + } + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::frameset) { + PARSE_ERROR(); + + if (m_stack_of_open_elements.elements().size() == 1 + || m_stack_of_open_elements.elements().at(1).local_name() != HTML::TagNames::body) { + ASSERT(m_parsing_fragment); + return; + } + + if (!m_frameset_ok) + return; + + TODO(); + } + + if (token.is_end_of_file()) { + if (!m_stack_of_template_insertion_modes.is_empty()) { + process_using_the_rules_for(InsertionMode::InTemplate, token); + return; + } + + for (auto& node : m_stack_of_open_elements.elements()) { + if (!node.local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr, HTML::TagNames::body, HTML::TagNames::html)) { + PARSE_ERROR(); + break; + } + } + + stop_parsing(); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::body) { + if (!m_stack_of_open_elements.has_in_scope(HTML::TagNames::body)) { + PARSE_ERROR(); + return; + } + + for (auto& node : m_stack_of_open_elements.elements()) { + if (!node.local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr, HTML::TagNames::body, HTML::TagNames::html)) { + PARSE_ERROR(); + break; + } + } + + m_insertion_mode = InsertionMode::AfterBody; + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::html) { + if (!m_stack_of_open_elements.has_in_scope(HTML::TagNames::body)) { + PARSE_ERROR(); + return; + } + + for (auto& node : m_stack_of_open_elements.elements()) { + if (!node.local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr, HTML::TagNames::body, HTML::TagNames::html)) { + PARSE_ERROR(); + break; + } + } + + m_insertion_mode = InsertionMode::AfterBody; + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::address, HTML::TagNames::article, HTML::TagNames::aside, HTML::TagNames::blockquote, HTML::TagNames::center, HTML::TagNames::details, HTML::TagNames::dialog, HTML::TagNames::dir, HTML::TagNames::div, HTML::TagNames::dl, HTML::TagNames::fieldset, HTML::TagNames::figcaption, HTML::TagNames::figure, HTML::TagNames::footer, HTML::TagNames::header, HTML::TagNames::hgroup, HTML::TagNames::main, HTML::TagNames::menu, HTML::TagNames::nav, HTML::TagNames::ol, HTML::TagNames::p, HTML::TagNames::section, HTML::TagNames::summary, HTML::TagNames::ul)) { + if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p)) + close_a_p_element(); + insert_html_element(token); + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::h1, HTML::TagNames::h2, HTML::TagNames::h3, HTML::TagNames::h4, HTML::TagNames::h5, HTML::TagNames::h6)) { + if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p)) + close_a_p_element(); + if (current_node().local_name().is_one_of(HTML::TagNames::h1, HTML::TagNames::h2, HTML::TagNames::h3, HTML::TagNames::h4, HTML::TagNames::h5, HTML::TagNames::h6)) { + PARSE_ERROR(); + m_stack_of_open_elements.pop(); + } + insert_html_element(token); + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::pre, HTML::TagNames::listing)) { + if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p)) + close_a_p_element(); + + insert_html_element(token); + + m_frameset_ok = false; + + // If the next token is a U+000A LINE FEED (LF) character token, + // then ignore that token and move on to the next one. + // (Newlines at the start of pre blocks are ignored as an authoring convenience.) + auto next_token = m_tokenizer.next_token(); + if (next_token.has_value() && next_token.value().is_character() && next_token.value().code_point() == '\n') { + // Ignore it. + } else { + process_using_the_rules_for(m_insertion_mode, next_token.value()); + } + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::form) { + if (m_form_element && !m_stack_of_open_elements.contains(HTML::TagNames::template_)) { + PARSE_ERROR(); + return; + } + if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p)) + close_a_p_element(); + auto element = insert_html_element(token); + if (!m_stack_of_open_elements.contains(HTML::TagNames::template_)) + m_form_element = downcast<HTMLFormElement>(*element); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::li) { + m_frameset_ok = false; + + for (ssize_t i = m_stack_of_open_elements.elements().size() - 1; i >= 0; --i) { + RefPtr<DOM::Element> node = m_stack_of_open_elements.elements()[i]; + + if (node->local_name() == HTML::TagNames::li) { + generate_implied_end_tags(HTML::TagNames::li); + if (current_node().local_name() != HTML::TagNames::li) { + PARSE_ERROR(); + } + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::li); + break; + } + + if (is_special_tag(node->local_name(), node->namespace_()) && !node->local_name().is_one_of(HTML::TagNames::address, HTML::TagNames::div, HTML::TagNames::p)) + break; + } + + if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p)) + close_a_p_element(); + + insert_html_element(token); + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt)) { + m_frameset_ok = false; + for (ssize_t i = m_stack_of_open_elements.elements().size() - 1; i >= 0; --i) { + RefPtr<DOM::Element> node = m_stack_of_open_elements.elements()[i]; + if (node->local_name() == HTML::TagNames::dd) { + generate_implied_end_tags(HTML::TagNames::dd); + if (current_node().local_name() != HTML::TagNames::dd) { + PARSE_ERROR(); + } + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::dd); + break; + } + if (node->local_name() == HTML::TagNames::dt) { + generate_implied_end_tags(HTML::TagNames::dt); + if (current_node().local_name() != HTML::TagNames::dt) { + PARSE_ERROR(); + } + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::dt); + break; + } + if (is_special_tag(node->local_name(), node->namespace_()) && !node->local_name().is_one_of(HTML::TagNames::address, HTML::TagNames::div, HTML::TagNames::p)) + break; + } + if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p)) + close_a_p_element(); + insert_html_element(token); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::plaintext) { + if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p)) + close_a_p_element(); + insert_html_element(token); + m_tokenizer.switch_to({}, HTMLTokenizer::State::PLAINTEXT); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::button) { + if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::button)) { + PARSE_ERROR(); + generate_implied_end_tags(); + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::button); + } + reconstruct_the_active_formatting_elements(); + insert_html_element(token); + m_frameset_ok = false; + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::address, HTML::TagNames::article, HTML::TagNames::aside, HTML::TagNames::blockquote, HTML::TagNames::button, HTML::TagNames::center, HTML::TagNames::details, HTML::TagNames::dialog, HTML::TagNames::dir, HTML::TagNames::div, HTML::TagNames::dl, HTML::TagNames::fieldset, HTML::TagNames::figcaption, HTML::TagNames::figure, HTML::TagNames::footer, HTML::TagNames::header, HTML::TagNames::hgroup, HTML::TagNames::listing, HTML::TagNames::main, HTML::TagNames::menu, HTML::TagNames::nav, HTML::TagNames::ol, HTML::TagNames::pre, HTML::TagNames::section, HTML::TagNames::summary, HTML::TagNames::ul)) { + if (!m_stack_of_open_elements.has_in_scope(token.tag_name())) { + PARSE_ERROR(); + return; + } + + generate_implied_end_tags(); + + if (current_node().local_name() != token.tag_name()) { + PARSE_ERROR(); + } + + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(token.tag_name()); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::form) { + if (!m_stack_of_open_elements.contains(HTML::TagNames::template_)) { + auto node = m_form_element; + m_form_element = nullptr; + if (!node || !m_stack_of_open_elements.has_in_scope(*node)) { + PARSE_ERROR(); + return; + } + generate_implied_end_tags(); + if (¤t_node() != node) { + PARSE_ERROR(); + } + m_stack_of_open_elements.elements().remove_first_matching([&](auto& entry) { return entry.ptr() == node.ptr(); }); + } else { + if (!m_stack_of_open_elements.has_in_scope(HTML::TagNames::form)) { + PARSE_ERROR(); + return; + } + generate_implied_end_tags(); + if (current_node().local_name() != HTML::TagNames::form) { + PARSE_ERROR(); + } + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::form); + } + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::p) { + if (!m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p)) { + PARSE_ERROR(); + insert_html_element(HTMLToken::make_start_tag(HTML::TagNames::p)); + } + close_a_p_element(); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::li) { + if (!m_stack_of_open_elements.has_in_list_item_scope(HTML::TagNames::li)) { + PARSE_ERROR(); + return; + } + generate_implied_end_tags(HTML::TagNames::li); + if (current_node().local_name() != HTML::TagNames::li) { + PARSE_ERROR(); + dbg() << "Expected <li> current node, but had <" << current_node().local_name() << ">"; + } + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::li); + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt)) { + if (!m_stack_of_open_elements.has_in_scope(token.tag_name())) { + PARSE_ERROR(); + return; + } + generate_implied_end_tags(token.tag_name()); + if (current_node().local_name() != token.tag_name()) { + PARSE_ERROR(); + } + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(token.tag_name()); + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::h1, HTML::TagNames::h2, HTML::TagNames::h3, HTML::TagNames::h4, HTML::TagNames::h5, HTML::TagNames::h6)) { + if (!m_stack_of_open_elements.has_in_scope(HTML::TagNames::h1) + && !m_stack_of_open_elements.has_in_scope(HTML::TagNames::h2) + && !m_stack_of_open_elements.has_in_scope(HTML::TagNames::h3) + && !m_stack_of_open_elements.has_in_scope(HTML::TagNames::h4) + && !m_stack_of_open_elements.has_in_scope(HTML::TagNames::h5) + && !m_stack_of_open_elements.has_in_scope(HTML::TagNames::h6)) { + PARSE_ERROR(); + return; + } + + generate_implied_end_tags(); + if (current_node().local_name() != token.tag_name()) { + PARSE_ERROR(); + } + + for (;;) { + auto popped_element = m_stack_of_open_elements.pop(); + if (popped_element->local_name().is_one_of(HTML::TagNames::h1, HTML::TagNames::h2, HTML::TagNames::h3, HTML::TagNames::h4, HTML::TagNames::h5, HTML::TagNames::h6)) + break; + } + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::a) { + if (auto* element = m_list_of_active_formatting_elements.last_element_with_tag_name_before_marker(HTML::TagNames::a)) { + PARSE_ERROR(); + if (run_the_adoption_agency_algorithm(token) == AdoptionAgencyAlgorithmOutcome::RunAnyOtherEndTagSteps) + goto AnyOtherEndTag; + m_list_of_active_formatting_elements.remove(*element); + m_stack_of_open_elements.elements().remove_first_matching([&](auto& entry) { + return entry.ptr() == element; + }); + } + reconstruct_the_active_formatting_elements(); + auto element = insert_html_element(token); + m_list_of_active_formatting_elements.add(*element); + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::b, HTML::TagNames::big, HTML::TagNames::code, HTML::TagNames::em, HTML::TagNames::font, HTML::TagNames::i, HTML::TagNames::s, HTML::TagNames::small, HTML::TagNames::strike, HTML::TagNames::strong, HTML::TagNames::tt, HTML::TagNames::u)) { + reconstruct_the_active_formatting_elements(); + auto element = insert_html_element(token); + m_list_of_active_formatting_elements.add(*element); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::nobr) { + reconstruct_the_active_formatting_elements(); + if (m_stack_of_open_elements.has_in_scope(HTML::TagNames::nobr)) { + PARSE_ERROR(); + run_the_adoption_agency_algorithm(token); + reconstruct_the_active_formatting_elements(); + } + auto element = insert_html_element(token); + m_list_of_active_formatting_elements.add(*element); + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::a, HTML::TagNames::b, HTML::TagNames::big, HTML::TagNames::code, HTML::TagNames::em, HTML::TagNames::font, HTML::TagNames::i, HTML::TagNames::nobr, HTML::TagNames::s, HTML::TagNames::small, HTML::TagNames::strike, HTML::TagNames::strong, HTML::TagNames::tt, HTML::TagNames::u)) { + if (run_the_adoption_agency_algorithm(token) == AdoptionAgencyAlgorithmOutcome::RunAnyOtherEndTagSteps) + goto AnyOtherEndTag; + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::applet, HTML::TagNames::marquee, HTML::TagNames::object)) { + reconstruct_the_active_formatting_elements(); + insert_html_element(token); + m_list_of_active_formatting_elements.add_marker(); + m_frameset_ok = false; + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::applet, HTML::TagNames::marquee, HTML::TagNames::object)) { + if (!m_stack_of_open_elements.has_in_scope(token.tag_name())) { + PARSE_ERROR(); + return; + } + + generate_implied_end_tags(); + if (current_node().local_name() != token.tag_name()) { + PARSE_ERROR(); + } + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(token.tag_name()); + m_list_of_active_formatting_elements.clear_up_to_the_last_marker(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::table) { + if (!document().in_quirks_mode()) { + if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p)) + close_a_p_element(); + } + insert_html_element(token); + m_frameset_ok = false; + m_insertion_mode = InsertionMode::InTable; + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::br) { + token.drop_attributes(); + goto BRStartTag; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::area, HTML::TagNames::br, HTML::TagNames::embed, HTML::TagNames::img, HTML::TagNames::keygen, HTML::TagNames::wbr)) { + BRStartTag: + reconstruct_the_active_formatting_elements(); + insert_html_element(token); + m_stack_of_open_elements.pop(); + token.acknowledge_self_closing_flag_if_set(); + m_frameset_ok = false; + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::input) { + reconstruct_the_active_formatting_elements(); + insert_html_element(token); + m_stack_of_open_elements.pop(); + token.acknowledge_self_closing_flag_if_set(); + auto type_attribute = token.attribute(HTML::AttributeNames::type); + if (type_attribute.is_null() || !type_attribute.equals_ignoring_case("hidden")) { + m_frameset_ok = false; + } + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::param, HTML::TagNames::source, HTML::TagNames::track)) { + insert_html_element(token); + m_stack_of_open_elements.pop(); + token.acknowledge_self_closing_flag_if_set(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::hr) { + if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p)) + close_a_p_element(); + insert_html_element(token); + m_stack_of_open_elements.pop(); + token.acknowledge_self_closing_flag_if_set(); + m_frameset_ok = false; + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::image) { + // Parse error. Change the token's tag name to HTML::TagNames::img and reprocess it. (Don't ask.) + PARSE_ERROR(); + token.m_tag.tag_name.clear(); + token.m_tag.tag_name.append(HTML::TagNames::img); + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::textarea) { + insert_html_element(token); + + m_tokenizer.switch_to({}, HTMLTokenizer::State::RCDATA); + + // If the next token is a U+000A LINE FEED (LF) character token, + // then ignore that token and move on to the next one. + // (Newlines at the start of pre blocks are ignored as an authoring convenience.) + auto next_token = m_tokenizer.next_token(); + + m_original_insertion_mode = m_insertion_mode; + m_frameset_ok = false; + m_insertion_mode = InsertionMode::Text; + + if (next_token.has_value() && next_token.value().is_character() && next_token.value().code_point() == '\n') { + // Ignore it. + } else { + process_using_the_rules_for(m_insertion_mode, next_token.value()); + } + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::xmp) { + if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p)) { + close_a_p_element(); + } + reconstruct_the_active_formatting_elements(); + m_frameset_ok = false; + parse_generic_raw_text_element(token); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::iframe) { + m_frameset_ok = false; + parse_generic_raw_text_element(token); + return; + } + + if (token.is_start_tag() && ((token.tag_name() == HTML::TagNames::noembed) || (token.tag_name() == HTML::TagNames::noscript && m_scripting_enabled))) { + parse_generic_raw_text_element(token); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::select) { + reconstruct_the_active_formatting_elements(); + insert_html_element(token); + m_frameset_ok = false; + switch (m_insertion_mode) { + case InsertionMode::InTable: + case InsertionMode::InCaption: + case InsertionMode::InTableBody: + case InsertionMode::InRow: + case InsertionMode::InCell: + m_insertion_mode = InsertionMode::InSelectInTable; + break; + default: + m_insertion_mode = InsertionMode::InSelect; + break; + } + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::optgroup, HTML::TagNames::option)) { + if (current_node().local_name() == HTML::TagNames::option) + m_stack_of_open_elements.pop(); + reconstruct_the_active_formatting_elements(); + insert_html_element(token); + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::rb, HTML::TagNames::rtc)) { + if (m_stack_of_open_elements.has_in_scope(HTML::TagNames::ruby)) + generate_implied_end_tags(); + + if (current_node().local_name() != HTML::TagNames::ruby) + PARSE_ERROR(); + + insert_html_element(token); + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::rp, HTML::TagNames::rt)) { + if (m_stack_of_open_elements.has_in_scope(HTML::TagNames::ruby)) + generate_implied_end_tags(HTML::TagNames::rtc); + + if (current_node().local_name() != HTML::TagNames::rtc || current_node().local_name() != HTML::TagNames::ruby) + PARSE_ERROR(); + + insert_html_element(token); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::math) { + reconstruct_the_active_formatting_elements(); + adjust_mathml_attributes(token); + adjust_foreign_attributes(token); + + insert_foreign_element(token, Namespace::MathML); + + if (token.is_self_closing()) { + m_stack_of_open_elements.pop(); + token.acknowledge_self_closing_flag_if_set(); + } + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::svg) { + reconstruct_the_active_formatting_elements(); + adjust_svg_attributes(token); + adjust_foreign_attributes(token); + + insert_foreign_element(token, Namespace::SVG); + + if (token.is_self_closing()) { + m_stack_of_open_elements.pop(); + token.acknowledge_self_closing_flag_if_set(); + } + return; + } + + if ((token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::frame, HTML::TagNames::head, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr))) { + PARSE_ERROR(); + return; + } + + // Any other start tag + if (token.is_start_tag()) { + reconstruct_the_active_formatting_elements(); + insert_html_element(token); + return; + } + + if (token.is_end_tag()) { + AnyOtherEndTag: + RefPtr<DOM::Element> node; + for (ssize_t i = m_stack_of_open_elements.elements().size() - 1; i >= 0; --i) { + node = m_stack_of_open_elements.elements()[i]; + if (node->local_name() == token.tag_name()) { + generate_implied_end_tags(token.tag_name()); + if (node != current_node()) { + PARSE_ERROR(); + } + while (¤t_node() != node) { + m_stack_of_open_elements.pop(); + } + m_stack_of_open_elements.pop(); + break; + } + if (is_special_tag(node->local_name(), node->namespace_())) { + PARSE_ERROR(); + return; + } + } + return; + } +} + +void HTMLDocumentParser::adjust_mathml_attributes(HTMLToken& token) +{ + token.adjust_attribute_name("definitionurl", "definitionURL"); +} + +void HTMLDocumentParser::adjust_svg_tag_names(HTMLToken& token) +{ + token.adjust_tag_name("altglyph", "altGlyph"); + token.adjust_tag_name("altglyphdef", "altGlyphDef"); + token.adjust_tag_name("altglyphitem", "altGlyphItem"); + token.adjust_tag_name("animatecolor", "animateColor"); + token.adjust_tag_name("animatemotion", "animateMotion"); + token.adjust_tag_name("animatetransform", "animateTransform"); + token.adjust_tag_name("clippath", "clipPath"); + token.adjust_tag_name("feblend", "feBlend"); + token.adjust_tag_name("fecolormatrix", "feColorMatrix"); + token.adjust_tag_name("fecomponenttransfer", "feComponentTransfer"); + token.adjust_tag_name("fecomposite", "feComposite"); + token.adjust_tag_name("feconvolvematrix", "feConvolveMatrix"); + token.adjust_tag_name("fediffuselighting", "feDiffuseLighting"); + token.adjust_tag_name("fedisplacementmap", "feDisplacementMap"); + token.adjust_tag_name("fedistantlight", "feDistantLight"); + token.adjust_tag_name("fedropshadow", "feDropShadow"); + token.adjust_tag_name("feflood", "feFlood"); + token.adjust_tag_name("fefunca", "feFuncA"); + token.adjust_tag_name("fefuncb", "feFuncB"); + token.adjust_tag_name("fefuncg", "feFuncG"); + token.adjust_tag_name("fefuncr", "feFuncR"); + token.adjust_tag_name("fegaussianblur", "feGaussianBlur"); + token.adjust_tag_name("feimage", "feImage"); + token.adjust_tag_name("femerge", "feMerge"); + token.adjust_tag_name("femergenode", "feMergeNode"); + token.adjust_tag_name("femorphology", "feMorphology"); + token.adjust_tag_name("feoffset", "feOffset"); + token.adjust_tag_name("fepointlight", "fePointLight"); + token.adjust_tag_name("fespecularlighting", "feSpecularLighting"); + token.adjust_tag_name("fespotlight", "feSpotlight"); + token.adjust_tag_name("glyphref", "glyphRef"); + token.adjust_tag_name("lineargradient", "linearGradient"); + token.adjust_tag_name("radialgradient", "radialGradient"); + token.adjust_tag_name("textpath", "textPath"); +} + +void HTMLDocumentParser::adjust_svg_attributes(HTMLToken& token) +{ + token.adjust_attribute_name("attributename", "attributeName"); + token.adjust_attribute_name("attributetype", "attributeType"); + token.adjust_attribute_name("basefrequency", "baseFrequency"); + token.adjust_attribute_name("baseprofile", "baseProfile"); + token.adjust_attribute_name("calcmode", "calcMode"); + token.adjust_attribute_name("clippathunits", "clipPathUnits"); + token.adjust_attribute_name("diffuseconstant", "diffuseConstant"); + token.adjust_attribute_name("edgemode", "edgeMode"); + token.adjust_attribute_name("filterunits", "filterUnits"); + token.adjust_attribute_name("glyphref", "glyphRef"); + token.adjust_attribute_name("gradienttransform", "gradientTransform"); + token.adjust_attribute_name("gradientunits", "gradientUnits"); + token.adjust_attribute_name("kernelmatrix", "kernelMatrix"); + token.adjust_attribute_name("kernelunitlength", "kernelUnitLength"); + token.adjust_attribute_name("keypoints", "keyPoints"); + token.adjust_attribute_name("keysplines", "keySplines"); + token.adjust_attribute_name("keytimes", "keyTimes"); + token.adjust_attribute_name("lengthadjust", "lengthAdjust"); + token.adjust_attribute_name("limitingconeangle", "limitingConeAngle"); + token.adjust_attribute_name("markerheight", "markerHeight"); + token.adjust_attribute_name("markerunits", "markerUnits"); + token.adjust_attribute_name("markerwidth", "markerWidth"); + token.adjust_attribute_name("maskcontentunits", "maskContentUnits"); + token.adjust_attribute_name("maskunits", "maskUnits"); + token.adjust_attribute_name("numoctaves", "numOctaves"); + token.adjust_attribute_name("pathlength", "pathLength"); + token.adjust_attribute_name("patterncontentunits", "patternContentUnits"); + token.adjust_attribute_name("patterntransform", "patternTransform"); + token.adjust_attribute_name("patternunits", "patternUnits"); + token.adjust_attribute_name("pointsatx", "pointsAtX"); + token.adjust_attribute_name("pointsaty", "pointsAtY"); + token.adjust_attribute_name("pointsatz", "pointsAtZ"); + token.adjust_attribute_name("preservealpha", "preserveAlpha"); + token.adjust_attribute_name("preserveaspectratio", "preserveAspectRatio"); + token.adjust_attribute_name("primitiveunits", "primitiveUnits"); + token.adjust_attribute_name("refx", "refX"); + token.adjust_attribute_name("refy", "refY"); + token.adjust_attribute_name("repeatcount", "repeatCount"); + token.adjust_attribute_name("repeatdur", "repeatDur"); + token.adjust_attribute_name("requiredextensions", "requiredExtensions"); + token.adjust_attribute_name("requiredfeatures", "requiredFeatures"); + token.adjust_attribute_name("specularconstant", "specularConstant"); + token.adjust_attribute_name("specularexponent", "specularExponent"); + token.adjust_attribute_name("spreadmethod", "spreadMethod"); + token.adjust_attribute_name("startoffset", "startOffset"); + token.adjust_attribute_name("stddeviation", "stdDeviation"); + token.adjust_attribute_name("stitchtiles", "stitchTiles"); + token.adjust_attribute_name("surfacescale", "surfaceScale"); + token.adjust_attribute_name("systemlanguage", "systemLanguage"); + token.adjust_attribute_name("tablevalues", "tableValues"); + token.adjust_attribute_name("targetx", "targetX"); + token.adjust_attribute_name("targety", "targetY"); + token.adjust_attribute_name("textlength", "textLength"); + token.adjust_attribute_name("viewbox", "viewBox"); + token.adjust_attribute_name("viewtarget", "viewTarget"); + token.adjust_attribute_name("xchannelselector", "xChannelSelector"); + token.adjust_attribute_name("ychannelselector", "yChannelSelector"); + token.adjust_attribute_name("zoomandpan", "zoomAndPan"); +} + +void HTMLDocumentParser::adjust_foreign_attributes(HTMLToken& token) +{ + token.adjust_foreign_attribute("xlink:actuate", "xlink", "actuate", Namespace::XLink); + token.adjust_foreign_attribute("xlink:arcrole", "xlink", "arcrole", Namespace::XLink); + token.adjust_foreign_attribute("xlink:href", "xlink", "href", Namespace::XLink); + token.adjust_foreign_attribute("xlink:role", "xlink", "role", Namespace::XLink); + token.adjust_foreign_attribute("xlink:show", "xlink", "show", Namespace::XLink); + token.adjust_foreign_attribute("xlink:title", "xlink", "title", Namespace::XLink); + token.adjust_foreign_attribute("xlink:type", "xlink", "type", Namespace::XLink); + + token.adjust_foreign_attribute("xml:lang", "xml", "lang", Namespace::XML); + token.adjust_foreign_attribute("xml:space", "xml", "space", Namespace::XML); + + token.adjust_foreign_attribute("xmlns", "", "xmlns", Namespace::XMLNS); + token.adjust_foreign_attribute("xmlns:xlink", "xmlns", "xlink", Namespace::XMLNS); +} + +void HTMLDocumentParser::increment_script_nesting_level() +{ + ++m_script_nesting_level; +} + +void HTMLDocumentParser::decrement_script_nesting_level() +{ + ASSERT(m_script_nesting_level); + --m_script_nesting_level; +} + +void HTMLDocumentParser::handle_text(HTMLToken& token) +{ + if (token.is_character()) { + insert_character(token.code_point()); + return; + } + if (token.is_end_of_file()) { + PARSE_ERROR(); + if (current_node().local_name() == HTML::TagNames::script) + downcast<HTMLScriptElement>(current_node()).set_already_started({}, true); + m_stack_of_open_elements.pop(); + m_insertion_mode = m_original_insertion_mode; + process_using_the_rules_for(m_insertion_mode, token); + return; + } + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::script) { + // Make sure the <script> element has up-to-date text content before preparing the script. + flush_character_insertions(); + + NonnullRefPtr<HTMLScriptElement> script = downcast<HTMLScriptElement>(current_node()); + m_stack_of_open_elements.pop(); + m_insertion_mode = m_original_insertion_mode; + // FIXME: Handle tokenizer insertion point stuff here. + increment_script_nesting_level(); + script->prepare_script({}); + decrement_script_nesting_level(); + if (script_nesting_level() == 0) + m_parser_pause_flag = false; + // FIXME: Handle tokenizer insertion point stuff here too. + + while (document().pending_parsing_blocking_script()) { + if (script_nesting_level() != 0) { + m_parser_pause_flag = true; + // FIXME: Abort the processing of any nested invocations of the tokenizer, + // yielding control back to the caller. (Tokenization will resume when + // the caller returns to the "outer" tree construction stage.) + TODO(); + } else { + auto the_script = document().take_pending_parsing_blocking_script({}); + m_tokenizer.set_blocked(true); + + // FIXME: If the parser's Document has a style sheet that is blocking scripts + // or the script's "ready to be parser-executed" flag is not set: + // spin the event loop until the parser's Document has no style sheet + // that is blocking scripts and the script's "ready to be parser-executed" + // flag is set. + + if (the_script->failed_to_load()) + return; + + ASSERT(the_script->is_ready_to_be_parser_executed()); + + if (m_aborted) + return; + + m_tokenizer.set_blocked(false); + + // FIXME: Handle tokenizer insertion point stuff here too. + + ASSERT(script_nesting_level() == 0); + increment_script_nesting_level(); + + the_script->execute_script(); + + decrement_script_nesting_level(); + ASSERT(script_nesting_level() == 0); + m_parser_pause_flag = false; + + // FIXME: Handle tokenizer insertion point stuff here too. + } + } + return; + } + + if (token.is_end_tag()) { + m_stack_of_open_elements.pop(); + m_insertion_mode = m_original_insertion_mode; + return; + } + TODO(); +} + +void HTMLDocumentParser::clear_the_stack_back_to_a_table_context() +{ + while (!current_node().local_name().is_one_of(HTML::TagNames::table, HTML::TagNames::template_, HTML::TagNames::html)) + m_stack_of_open_elements.pop(); + + if (current_node().local_name() == HTML::TagNames::html) + ASSERT(m_parsing_fragment); +} + +void HTMLDocumentParser::clear_the_stack_back_to_a_table_row_context() +{ + while (!current_node().local_name().is_one_of(HTML::TagNames::tr, HTML::TagNames::template_, HTML::TagNames::html)) + m_stack_of_open_elements.pop(); + + if (current_node().local_name() == HTML::TagNames::html) + ASSERT(m_parsing_fragment); +} + +void HTMLDocumentParser::clear_the_stack_back_to_a_table_body_context() +{ + while (!current_node().local_name().is_one_of(HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::template_, HTML::TagNames::html)) + m_stack_of_open_elements.pop(); + + if (current_node().local_name() == HTML::TagNames::html) + ASSERT(m_parsing_fragment); +} + +void HTMLDocumentParser::handle_in_row(HTMLToken& token) +{ + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::th, HTML::TagNames::td)) { + clear_the_stack_back_to_a_table_row_context(); + insert_html_element(token); + m_insertion_mode = InsertionMode::InCell; + m_list_of_active_formatting_elements.add_marker(); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::tr) { + if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::tr)) { + PARSE_ERROR(); + return; + } + clear_the_stack_back_to_a_table_row_context(); + m_stack_of_open_elements.pop(); + m_insertion_mode = InsertionMode::InTableBody; + return; + } + + if ((token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::tr)) + || (token.is_end_tag() && token.tag_name() == HTML::TagNames::table)) { + if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::tr)) { + PARSE_ERROR(); + return; + } + clear_the_stack_back_to_a_table_row_context(); + m_stack_of_open_elements.pop(); + m_insertion_mode = InsertionMode::InTableBody; + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead)) { + if (!m_stack_of_open_elements.has_in_table_scope(token.tag_name())) { + PARSE_ERROR(); + return; + } + if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::tr)) { + return; + } + clear_the_stack_back_to_a_table_row_context(); + m_stack_of_open_elements.pop(); + m_insertion_mode = InsertionMode::InTableBody; + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::body, HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::html, HTML::TagNames::td, HTML::TagNames::th)) { + PARSE_ERROR(); + return; + } + + process_using_the_rules_for(InsertionMode::InTable, token); +} + +void HTMLDocumentParser::close_the_cell() +{ + generate_implied_end_tags(); + if (!current_node().local_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th)) { + PARSE_ERROR(); + } + while (!current_node().local_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th)) + m_stack_of_open_elements.pop(); + m_stack_of_open_elements.pop(); + m_list_of_active_formatting_elements.clear_up_to_the_last_marker(); + m_insertion_mode = InsertionMode::InRow; +} + +void HTMLDocumentParser::handle_in_cell(HTMLToken& token) +{ + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th)) { + if (!m_stack_of_open_elements.has_in_table_scope(token.tag_name())) { + PARSE_ERROR(); + return; + } + generate_implied_end_tags(); + + if (current_node().local_name() != token.tag_name()) { + PARSE_ERROR(); + } + + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(token.tag_name()); + + m_list_of_active_formatting_elements.clear_up_to_the_last_marker(); + + m_insertion_mode = InsertionMode::InRow; + return; + } + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr)) { + if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::td) && !m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::th)) { + ASSERT(m_parsing_fragment); + PARSE_ERROR(); + return; + } + close_the_cell(); + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::body, HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::html)) { + PARSE_ERROR(); + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::table, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::tr)) { + if (!m_stack_of_open_elements.has_in_table_scope(token.tag_name())) { + PARSE_ERROR(); + return; + } + close_the_cell(); + // Reprocess the token. + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + process_using_the_rules_for(InsertionMode::InBody, token); +} + +void HTMLDocumentParser::handle_in_table_text(HTMLToken& token) +{ + if (token.is_character()) { + if (token.code_point() == 0) { + PARSE_ERROR(); + return; + } + + m_pending_table_character_tokens.append(token); + return; + } + + for (auto& pending_token : m_pending_table_character_tokens) { + ASSERT(pending_token.is_character()); + if (!pending_token.is_parser_whitespace()) { + // If any of the tokens in the pending table character tokens list + // are character tokens that are not ASCII whitespace, then this is a parse error: + // reprocess the character tokens in the pending table character tokens list using + // the rules given in the "anything else" entry in the "in table" insertion mode. + PARSE_ERROR(); + m_foster_parenting = true; + process_using_the_rules_for(InsertionMode::InBody, token); + m_foster_parenting = false; + return; + } + } + + for (auto& pending_token : m_pending_table_character_tokens) { + insert_character(pending_token.code_point()); + } + + m_insertion_mode = m_original_insertion_mode; + process_using_the_rules_for(m_insertion_mode, token); +} + +void HTMLDocumentParser::handle_in_table_body(HTMLToken& token) +{ + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::tr) { + clear_the_stack_back_to_a_table_body_context(); + insert_html_element(token); + m_insertion_mode = InsertionMode::InRow; + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::th, HTML::TagNames::td)) { + PARSE_ERROR(); + clear_the_stack_back_to_a_table_body_context(); + insert_html_element(HTMLToken::make_start_tag(HTML::TagNames::tr)); + m_insertion_mode = InsertionMode::InRow; + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead)) { + if (!m_stack_of_open_elements.has_in_table_scope(token.tag_name())) { + PARSE_ERROR(); + return; + } + clear_the_stack_back_to_a_table_body_context(); + m_stack_of_open_elements.pop(); + m_insertion_mode = InsertionMode::InTable; + return; + } + + if ((token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead)) + || (token.is_end_tag() && token.tag_name() == HTML::TagNames::table)) { + + if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::tbody) + && !m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::thead) + && !m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::tfoot)) { + PARSE_ERROR(); + return; + } + + clear_the_stack_back_to_a_table_body_context(); + m_stack_of_open_elements.pop(); + m_insertion_mode = InsertionMode::InTable; + process_using_the_rules_for(InsertionMode::InTable, token); + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::body, HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::html, HTML::TagNames::td, HTML::TagNames::th, HTML::TagNames::tr)) { + PARSE_ERROR(); + return; + } + + process_using_the_rules_for(InsertionMode::InTable, token); +} + +void HTMLDocumentParser::handle_in_table(HTMLToken& token) +{ + if (token.is_character() && current_node().local_name().is_one_of(HTML::TagNames::table, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::tr)) { + m_pending_table_character_tokens.clear(); + m_original_insertion_mode = m_insertion_mode; + m_insertion_mode = InsertionMode::InTableText; + process_using_the_rules_for(InsertionMode::InTableText, token); + return; + } + if (token.is_comment()) { + insert_comment(token); + return; + } + if (token.is_doctype()) { + PARSE_ERROR(); + return; + } + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::caption) { + clear_the_stack_back_to_a_table_context(); + m_list_of_active_formatting_elements.add_marker(); + insert_html_element(token); + m_insertion_mode = InsertionMode::InCaption; + return; + } + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::colgroup) { + clear_the_stack_back_to_a_table_context(); + insert_html_element(token); + m_insertion_mode = InsertionMode::InColumnGroup; + return; + } + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::col) { + clear_the_stack_back_to_a_table_context(); + insert_html_element(HTMLToken::make_start_tag(HTML::TagNames::colgroup)); + m_insertion_mode = InsertionMode::InColumnGroup; + process_using_the_rules_for(m_insertion_mode, token); + return; + } + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead)) { + clear_the_stack_back_to_a_table_context(); + insert_html_element(token); + m_insertion_mode = InsertionMode::InTableBody; + return; + } + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th, HTML::TagNames::tr)) { + clear_the_stack_back_to_a_table_context(); + insert_html_element(HTMLToken::make_start_tag(HTML::TagNames::tbody)); + m_insertion_mode = InsertionMode::InTableBody; + process_using_the_rules_for(m_insertion_mode, token); + return; + } + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::table) { + PARSE_ERROR(); + if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::table)) + return; + + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::table); + + reset_the_insertion_mode_appropriately(); + process_using_the_rules_for(m_insertion_mode, token); + return; + } + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::table) { + if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::table)) { + PARSE_ERROR(); + return; + } + + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::table); + + reset_the_insertion_mode_appropriately(); + return; + } + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::body, HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::html, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr)) { + PARSE_ERROR(); + return; + } + if ((token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::style, HTML::TagNames::script, HTML::TagNames::template_)) + || (token.is_end_tag() && token.tag_name() == HTML::TagNames::template_)) { + process_using_the_rules_for(InsertionMode::InHead, token); + return; + } + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::input) { + auto type_attribute = token.attribute(HTML::AttributeNames::type); + if (type_attribute.is_null() || !type_attribute.equals_ignoring_case("hidden")) { + goto AnythingElse; + } + + PARSE_ERROR(); + insert_html_element(token); + + // FIXME: Is this the correct interpretation of "Pop that input element off the stack of open elements."? + // Because this wording is the first time it's seen in the spec. + // Other times it's worded as: "Immediately pop the current node off the stack of open elements." + m_stack_of_open_elements.pop(); + token.acknowledge_self_closing_flag_if_set(); + return; + } + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::form) { + PARSE_ERROR(); + if (m_form_element || m_stack_of_open_elements.contains(HTML::TagNames::template_)) { + return; + } + + m_form_element = downcast<HTMLFormElement>(*insert_html_element(token)); + + // FIXME: See previous FIXME, as this is the same situation but for form. + m_stack_of_open_elements.pop(); + return; + } + if (token.is_end_of_file()) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + +AnythingElse: + PARSE_ERROR(); + m_foster_parenting = true; + process_using_the_rules_for(InsertionMode::InBody, token); + m_foster_parenting = false; +} + +void HTMLDocumentParser::handle_in_select_in_table(HTMLToken& token) +{ + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::table, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::tr, HTML::TagNames::td, HTML::TagNames::th)) { + PARSE_ERROR(); + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select); + reset_the_insertion_mode_appropriately(); + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::table, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::tr, HTML::TagNames::td, HTML::TagNames::th)) { + PARSE_ERROR(); + + if (!m_stack_of_open_elements.has_in_table_scope(token.tag_name())) + return; + + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select); + reset_the_insertion_mode_appropriately(); + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + process_using_the_rules_for(InsertionMode::InSelect, token); +} + +void HTMLDocumentParser::handle_in_select(HTMLToken& token) +{ + if (token.is_character()) { + if (token.code_point() == 0) { + PARSE_ERROR(); + return; + } + insert_character(token.code_point()); + return; + } + + if (token.is_comment()) { + insert_comment(token); + return; + } + + if (token.is_doctype()) { + PARSE_ERROR(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::option) { + if (current_node().local_name() == HTML::TagNames::option) { + m_stack_of_open_elements.pop(); + } + insert_html_element(token); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::optgroup) { + if (current_node().local_name() == HTML::TagNames::option) { + m_stack_of_open_elements.pop(); + } + if (current_node().local_name() == HTML::TagNames::optgroup) { + m_stack_of_open_elements.pop(); + } + insert_html_element(token); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::optgroup) { + if (current_node().local_name() == HTML::TagNames::option && node_before_current_node().local_name() == HTML::TagNames::optgroup) + m_stack_of_open_elements.pop(); + + if (current_node().local_name() == HTML::TagNames::optgroup) { + m_stack_of_open_elements.pop(); + } else { + PARSE_ERROR(); + return; + } + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::option) { + if (current_node().local_name() == HTML::TagNames::option) { + m_stack_of_open_elements.pop(); + } else { + PARSE_ERROR(); + return; + } + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::select) { + if (!m_stack_of_open_elements.has_in_select_scope(HTML::TagNames::select)) { + ASSERT(m_parsing_fragment); + PARSE_ERROR(); + return; + } + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select); + reset_the_insertion_mode_appropriately(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::select) { + PARSE_ERROR(); + + if (!m_stack_of_open_elements.has_in_select_scope(HTML::TagNames::select)) { + ASSERT(m_parsing_fragment); + return; + } + + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select); + reset_the_insertion_mode_appropriately(); + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::input, HTML::TagNames::keygen, HTML::TagNames::textarea)) { + PARSE_ERROR(); + + if (!m_stack_of_open_elements.has_in_select_scope(HTML::TagNames::select)) { + ASSERT(m_parsing_fragment); + return; + } + + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select); + reset_the_insertion_mode_appropriately(); + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::script, HTML::TagNames::template_)) { + process_using_the_rules_for(InsertionMode::InHead, token); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::template_) { + process_using_the_rules_for(InsertionMode::InHead, token); + return; + } + + if (token.is_end_of_file()) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + + PARSE_ERROR(); +} + +void HTMLDocumentParser::handle_in_caption(HTMLToken& token) +{ + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::caption) { + if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::caption)) { + ASSERT(m_parsing_fragment); + PARSE_ERROR(); + return; + } + + generate_implied_end_tags(); + + if (current_node().local_name() != HTML::TagNames::caption) + PARSE_ERROR(); + + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::caption); + m_list_of_active_formatting_elements.clear_up_to_the_last_marker(); + + m_insertion_mode = InsertionMode::InTable; + return; + } + + if ((token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr)) + || (token.is_end_tag() && token.tag_name() == HTML::TagNames::table)) { + if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::caption)) { + ASSERT(m_parsing_fragment); + PARSE_ERROR(); + return; + } + + generate_implied_end_tags(); + + if (current_node().local_name() != HTML::TagNames::caption) + PARSE_ERROR(); + + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::caption); + m_list_of_active_formatting_elements.clear_up_to_the_last_marker(); + + m_insertion_mode = InsertionMode::InTable; + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::body, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::html, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr)) { + PARSE_ERROR(); + return; + } + + process_using_the_rules_for(InsertionMode::InBody, token); +} + +void HTMLDocumentParser::handle_in_column_group(HTMLToken& token) +{ + if (token.is_character() && token.is_parser_whitespace()) { + insert_character(token.code_point()); + return; + } + + if (token.is_comment()) { + insert_comment(token); + return; + } + + if (token.is_doctype()) { + PARSE_ERROR(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::col) { + insert_html_element(token); + m_stack_of_open_elements.pop(); + token.acknowledge_self_closing_flag_if_set(); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::colgroup) { + if (current_node().local_name() != HTML::TagNames::colgroup) { + PARSE_ERROR(); + return; + } + + m_stack_of_open_elements.pop(); + m_insertion_mode = InsertionMode::InTable; + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::col) { + PARSE_ERROR(); + return; + } + + if ((token.is_start_tag() || token.is_end_tag()) && token.tag_name() == HTML::TagNames::template_) { + process_using_the_rules_for(InsertionMode::InHead, token); + return; + } + + if (token.is_end_of_file()) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + + if (current_node().local_name() != HTML::TagNames::colgroup) { + PARSE_ERROR(); + return; + } + + m_stack_of_open_elements.pop(); + m_insertion_mode = InsertionMode::InTable; + process_using_the_rules_for(m_insertion_mode, token); +} + +void HTMLDocumentParser::handle_in_template(HTMLToken& token) +{ + if (token.is_character() || token.is_comment() || token.is_doctype()) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::base, HTML::TagNames::basefont, HTML::TagNames::bgsound, HTML::TagNames::link, HTML::TagNames::meta, HTML::TagNames::noframes, HTML::TagNames::script, HTML::TagNames::style, HTML::TagNames::template_, HTML::TagNames::title)) { + process_using_the_rules_for(InsertionMode::InHead, token); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::template_) { + process_using_the_rules_for(InsertionMode::InHead, token); + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::colgroup, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead)) { + m_stack_of_template_insertion_modes.take_last(); + m_stack_of_template_insertion_modes.append(InsertionMode::InTable); + m_insertion_mode = InsertionMode::InTable; + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::col) { + m_stack_of_template_insertion_modes.take_last(); + m_stack_of_template_insertion_modes.append(InsertionMode::InColumnGroup); + m_insertion_mode = InsertionMode::InColumnGroup; + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::tr) { + m_stack_of_template_insertion_modes.take_last(); + m_stack_of_template_insertion_modes.append(InsertionMode::InTableBody); + m_insertion_mode = InsertionMode::InTableBody; + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th)) { + m_stack_of_template_insertion_modes.take_last(); + m_stack_of_template_insertion_modes.append(InsertionMode::InRow); + m_insertion_mode = InsertionMode::InRow; + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + if (token.is_start_tag()) { + m_stack_of_template_insertion_modes.take_last(); + m_stack_of_template_insertion_modes.append(InsertionMode::InBody); + m_insertion_mode = InsertionMode::InBody; + process_using_the_rules_for(m_insertion_mode, token); + return; + } + + if (token.is_end_tag()) { + PARSE_ERROR(); + return; + } + + if (token.is_end_of_file()) { + if (!m_stack_of_open_elements.contains(HTML::TagNames::template_)) { + ASSERT(m_parsing_fragment); + stop_parsing(); + return; + } + + PARSE_ERROR(); + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::template_); + m_list_of_active_formatting_elements.clear_up_to_the_last_marker(); + m_stack_of_template_insertion_modes.take_last(); + reset_the_insertion_mode_appropriately(); + process_using_the_rules_for(m_insertion_mode, token); + } +} + +void HTMLDocumentParser::handle_in_frameset(HTMLToken& token) +{ + if (token.is_character() && token.is_parser_whitespace()) { + insert_character(token.code_point()); + return; + } + + if (token.is_comment()) { + insert_comment(token); + return; + } + + if (token.is_doctype()) { + PARSE_ERROR(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::frameset) { + insert_html_element(token); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::frameset) { + // FIXME: If the current node is the root html element, then this is a parse error; ignore the token. (fragment case) + + m_stack_of_open_elements.pop(); + + if (!m_parsing_fragment && current_node().local_name() != HTML::TagNames::frameset) { + m_insertion_mode = InsertionMode::AfterFrameset; + } + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::frame) { + insert_html_element(token); + m_stack_of_open_elements.pop(); + token.acknowledge_self_closing_flag_if_set(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::noframes) { + process_using_the_rules_for(InsertionMode::InHead, token); + return; + } + + if (token.is_end_of_file()) { + //FIXME: If the current node is not the root html element, then this is a parse error. + + stop_parsing(); + return; + } + + PARSE_ERROR(); +} + +void HTMLDocumentParser::handle_after_frameset(HTMLToken& token) +{ + if (token.is_character() && token.is_parser_whitespace()) { + insert_character(token.code_point()); + return; + } + + if (token.is_comment()) { + insert_comment(token); + return; + } + + if (token.is_doctype()) { + PARSE_ERROR(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + + if (token.is_end_tag() && token.tag_name() == HTML::TagNames::html) { + m_insertion_mode = InsertionMode::AfterAfterFrameset; + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::noframes) { + process_using_the_rules_for(InsertionMode::InHead, token); + return; + } + + if (token.is_end_of_file()) { + stop_parsing(); + return; + } + + PARSE_ERROR(); +} + +void HTMLDocumentParser::handle_after_after_frameset(HTMLToken& token) +{ + if (token.is_comment()) { + auto comment = adopt(*new DOM::Comment(document(), token.m_comment_or_character.data.to_string())); + document().append_child(move(comment)); + return; + } + + if (token.is_doctype() || token.is_parser_whitespace() || (token.is_start_tag() && token.tag_name() == HTML::TagNames::html)) { + process_using_the_rules_for(InsertionMode::InBody, token); + return; + } + + if (token.is_end_of_file()) { + stop_parsing(); + return; + } + + if (token.is_start_tag() && token.tag_name() == HTML::TagNames::noframes) { + process_using_the_rules_for(InsertionMode::InHead, token); + return; + } + + PARSE_ERROR(); +} + +void HTMLDocumentParser::process_using_the_rules_for_foreign_content(HTMLToken& token) +{ + if (token.is_character()) { + if (token.code_point() == 0) { + PARSE_ERROR(); + insert_character(0xFFFD); + return; + } + if (token.is_parser_whitespace()) { + insert_character(token.code_point()); + return; + } + insert_character(token.code_point()); + m_frameset_ok = false; + return; + } + + if (token.is_comment()) { + insert_comment(token); + return; + } + + if (token.is_doctype()) { + PARSE_ERROR(); + return; + } + + if ((token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::b, HTML::TagNames::big, HTML::TagNames::blockquote, HTML::TagNames::body, HTML::TagNames::br, HTML::TagNames::center, HTML::TagNames::code, HTML::TagNames::dd, HTML::TagNames::div, HTML::TagNames::dl, HTML::TagNames::dt, HTML::TagNames::em, HTML::TagNames::embed, HTML::TagNames::h1, HTML::TagNames::h2, HTML::TagNames::h3, HTML::TagNames::h4, HTML::TagNames::h5, HTML::TagNames::h6, HTML::TagNames::head, HTML::TagNames::hr, HTML::TagNames::i, HTML::TagNames::img, HTML::TagNames::li, HTML::TagNames::listing, HTML::TagNames::menu, HTML::TagNames::meta, HTML::TagNames::nobr, HTML::TagNames::ol, HTML::TagNames::p, HTML::TagNames::pre, HTML::TagNames::ruby, HTML::TagNames::s, HTML::TagNames::small, HTML::TagNames::span, HTML::TagNames::strong, HTML::TagNames::strike, HTML::TagNames::sub, HTML::TagNames::sup, HTML::TagNames::table, HTML::TagNames::tt, HTML::TagNames::u, HTML::TagNames::ul, HTML::TagNames::var)) + || (token.is_start_tag() && token.tag_name() == HTML::TagNames::font && (token.has_attribute(HTML::AttributeNames::color) || token.has_attribute(HTML::AttributeNames::face) || token.has_attribute(HTML::AttributeNames::size)))) { + PARSE_ERROR(); + if (m_parsing_fragment) { + goto AnyOtherStartTag; + } + + TODO(); + } + + if (token.is_start_tag()) { + AnyOtherStartTag: + if (adjusted_current_node().namespace_() == Namespace::MathML) { + adjust_mathml_attributes(token); + } else if (adjusted_current_node().namespace_() == Namespace::SVG) { + adjust_svg_tag_names(token); + adjust_svg_attributes(token); + } + + adjust_foreign_attributes(token); + insert_foreign_element(token, adjusted_current_node().namespace_()); + + if (token.is_self_closing()) { + if (token.tag_name() == SVG::TagNames::script && current_node().namespace_() == Namespace::SVG) { + token.acknowledge_self_closing_flag_if_set(); + goto ScriptEndTag; + } + + m_stack_of_open_elements.pop(); + token.acknowledge_self_closing_flag_if_set(); + } + + return; + } + + if (token.is_end_tag() && current_node().namespace_() == Namespace::SVG && current_node().tag_name() == SVG::TagNames::script) { + ScriptEndTag: + m_stack_of_open_elements.pop(); + TODO(); + } + + if (token.is_end_tag()) { + RefPtr<DOM::Element> node = current_node(); + // FIXME: Not sure if this is the correct to_lowercase, as the specification says "to ASCII lowercase" + if (node->tag_name().to_lowercase() != token.tag_name()) + PARSE_ERROR(); + for (ssize_t i = m_stack_of_open_elements.elements().size() - 1; i >= 0; --i) { + if (node == m_stack_of_open_elements.first()) { + ASSERT(m_parsing_fragment); + return; + } + // FIXME: See the above FIXME + if (node->tag_name().to_lowercase() == token.tag_name()) { + while (current_node() != node) + m_stack_of_open_elements.pop(); + m_stack_of_open_elements.pop(); + return; + } + + node = m_stack_of_open_elements.elements().at(i - 1); + + if (node->namespace_() != Namespace::HTML) + continue; + + process_using_the_rules_for(m_insertion_mode, token); + return; + } + } + + ASSERT_NOT_REACHED(); +} + +void HTMLDocumentParser::reset_the_insertion_mode_appropriately() +{ + for (ssize_t i = m_stack_of_open_elements.elements().size() - 1; i >= 0; --i) { + bool last = i == 0; + // NOTE: When parsing fragments, we substitute the context element for the root of the stack of open elements. + RefPtr<DOM::Element> node; + if (last && m_parsing_fragment) { + node = m_context_element; + } else { + node = m_stack_of_open_elements.elements().at(i); + } + + if (node->local_name() == HTML::TagNames::select) { + TODO(); + } + + if (!last && node->local_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th)) { + m_insertion_mode = InsertionMode::InCell; + return; + } + + if (node->local_name() == HTML::TagNames::tr) { + m_insertion_mode = InsertionMode::InRow; + return; + } + + if (node->local_name().is_one_of(HTML::TagNames::tbody, HTML::TagNames::thead, HTML::TagNames::tfoot)) { + m_insertion_mode = InsertionMode::InTableBody; + return; + } + + if (node->local_name() == HTML::TagNames::caption) { + m_insertion_mode = InsertionMode::InCaption; + return; + } + + if (node->local_name() == HTML::TagNames::colgroup) { + m_insertion_mode = InsertionMode::InColumnGroup; + return; + } + + if (node->local_name() == HTML::TagNames::table) { + m_insertion_mode = InsertionMode::InTable; + return; + } + + if (node->local_name() == HTML::TagNames::template_) { + m_insertion_mode = m_stack_of_template_insertion_modes.last(); + return; + } + + if (!last && node->local_name() == HTML::TagNames::head) { + m_insertion_mode = InsertionMode::InHead; + return; + } + + if (node->local_name() == HTML::TagNames::body) { + m_insertion_mode = InsertionMode::InBody; + return; + } + + if (node->local_name() == HTML::TagNames::frameset) { + ASSERT(m_parsing_fragment); + m_insertion_mode = InsertionMode::InFrameset; + return; + } + + if (node->local_name() == HTML::TagNames::html) { + if (!m_head_element) { + ASSERT(m_parsing_fragment); + m_insertion_mode = InsertionMode::BeforeHead; + return; + } + + m_insertion_mode = InsertionMode::AfterHead; + return; + } + } + + ASSERT(m_parsing_fragment); + m_insertion_mode = InsertionMode::InBody; +} + +const char* HTMLDocumentParser::insertion_mode_name() const +{ + switch (m_insertion_mode) { +#define __ENUMERATE_INSERTION_MODE(mode) \ + case InsertionMode::mode: \ + return #mode; + ENUMERATE_INSERTION_MODES +#undef __ENUMERATE_INSERTION_MODE + } + ASSERT_NOT_REACHED(); +} + +DOM::Document& HTMLDocumentParser::document() +{ + return *m_document; +} + +NonnullRefPtrVector<DOM::Node> HTMLDocumentParser::parse_html_fragment(DOM::Element& context_element, const StringView& markup) +{ + auto temp_document = DOM::Document::create(); + HTMLDocumentParser parser(*temp_document, markup, "utf-8"); + parser.m_context_element = context_element; + parser.m_parsing_fragment = true; + parser.document().set_quirks_mode(context_element.document().mode()); + + if (context_element.local_name().is_one_of(HTML::TagNames::title, HTML::TagNames::textarea)) { + parser.m_tokenizer.switch_to({}, HTMLTokenizer::State::RCDATA); + } else if (context_element.local_name().is_one_of(HTML::TagNames::style, HTML::TagNames::xmp, HTML::TagNames::iframe, HTML::TagNames::noembed, HTML::TagNames::noframes)) { + parser.m_tokenizer.switch_to({}, HTMLTokenizer::State::RAWTEXT); + } else if (context_element.local_name().is_one_of(HTML::TagNames::script)) { + parser.m_tokenizer.switch_to({}, HTMLTokenizer::State::ScriptData); + } else if (context_element.local_name().is_one_of(HTML::TagNames::noscript)) { + if (context_element.document().is_scripting_enabled()) + parser.m_tokenizer.switch_to({}, HTMLTokenizer::State::RAWTEXT); + } else if (context_element.local_name().is_one_of(HTML::TagNames::plaintext)) { + parser.m_tokenizer.switch_to({}, HTMLTokenizer::State::PLAINTEXT); + } + + auto root = create_element(context_element.document(), HTML::TagNames::html, Namespace::HTML); + parser.document().append_child(root); + parser.m_stack_of_open_elements.push(root); + + if (context_element.local_name() == HTML::TagNames::template_) { + parser.m_stack_of_template_insertion_modes.append(InsertionMode::InTemplate); + } + + // FIXME: Create a start tag token whose name is the local name of context and whose attributes are the attributes of context. + + parser.reset_the_insertion_mode_appropriately(); + + for (auto* form_candidate = &context_element; form_candidate; form_candidate = form_candidate->parent_element()) { + if (is<HTMLFormElement>(*form_candidate)) { + parser.m_form_element = downcast<HTMLFormElement>(*form_candidate); + break; + } + } + + parser.run(context_element.document().url()); + + NonnullRefPtrVector<DOM::Node> children; + while (RefPtr<DOM::Node> child = root->first_child()) { + root->remove_child(*child); + context_element.document().adopt_node(*child); + children.append(*child); + } + return children; +} +} diff --git a/Userland/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.h b/Userland/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.h new file mode 100644 index 0000000000..4abbb74b72 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.h @@ -0,0 +1,193 @@ +/* + * 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 <AK/NonnullRefPtrVector.h> +#include <LibWeb/DOM/Node.h> +#include <LibWeb/HTML/Parser/HTMLTokenizer.h> +#include <LibWeb/HTML/Parser/ListOfActiveFormattingElements.h> +#include <LibWeb/HTML/Parser/StackOfOpenElements.h> + +namespace Web::HTML { + +#define ENUMERATE_INSERTION_MODES \ + __ENUMERATE_INSERTION_MODE(Initial) \ + __ENUMERATE_INSERTION_MODE(BeforeHTML) \ + __ENUMERATE_INSERTION_MODE(BeforeHead) \ + __ENUMERATE_INSERTION_MODE(InHead) \ + __ENUMERATE_INSERTION_MODE(InHeadNoscript) \ + __ENUMERATE_INSERTION_MODE(AfterHead) \ + __ENUMERATE_INSERTION_MODE(InBody) \ + __ENUMERATE_INSERTION_MODE(Text) \ + __ENUMERATE_INSERTION_MODE(InTable) \ + __ENUMERATE_INSERTION_MODE(InTableText) \ + __ENUMERATE_INSERTION_MODE(InCaption) \ + __ENUMERATE_INSERTION_MODE(InColumnGroup) \ + __ENUMERATE_INSERTION_MODE(InTableBody) \ + __ENUMERATE_INSERTION_MODE(InRow) \ + __ENUMERATE_INSERTION_MODE(InCell) \ + __ENUMERATE_INSERTION_MODE(InSelect) \ + __ENUMERATE_INSERTION_MODE(InSelectInTable) \ + __ENUMERATE_INSERTION_MODE(InTemplate) \ + __ENUMERATE_INSERTION_MODE(AfterBody) \ + __ENUMERATE_INSERTION_MODE(InFrameset) \ + __ENUMERATE_INSERTION_MODE(AfterFrameset) \ + __ENUMERATE_INSERTION_MODE(AfterAfterBody) \ + __ENUMERATE_INSERTION_MODE(AfterAfterFrameset) + +RefPtr<DOM::Document> parse_html_document(const StringView&, const URL&, const String& encoding); + +class HTMLDocumentParser { +public: + HTMLDocumentParser(DOM::Document&, const StringView& input, const String& encoding); + ~HTMLDocumentParser(); + + void run(const URL&); + + DOM::Document& document(); + + static NonnullRefPtrVector<DOM::Node> parse_html_fragment(DOM::Element& context_element, const StringView&); + + enum class InsertionMode { +#define __ENUMERATE_INSERTION_MODE(mode) mode, + ENUMERATE_INSERTION_MODES +#undef __ENUMERATE_INSERTION_MODE + }; + + InsertionMode insertion_mode() const { return m_insertion_mode; } + + static bool is_special_tag(const FlyString& tag_name, const FlyString& namespace_); + +private: + const char* insertion_mode_name() const; + + DOM::QuirksMode which_quirks_mode(const HTMLToken&) const; + + void handle_initial(HTMLToken&); + void handle_before_html(HTMLToken&); + void handle_before_head(HTMLToken&); + void handle_in_head(HTMLToken&); + void handle_in_head_noscript(HTMLToken&); + void handle_after_head(HTMLToken&); + void handle_in_body(HTMLToken&); + void handle_after_body(HTMLToken&); + void handle_after_after_body(HTMLToken&); + void handle_text(HTMLToken&); + void handle_in_table(HTMLToken&); + void handle_in_table_body(HTMLToken&); + void handle_in_row(HTMLToken&); + void handle_in_cell(HTMLToken&); + void handle_in_table_text(HTMLToken&); + void handle_in_select_in_table(HTMLToken&); + void handle_in_select(HTMLToken&); + void handle_in_caption(HTMLToken&); + void handle_in_column_group(HTMLToken&); + void handle_in_template(HTMLToken&); + void handle_in_frameset(HTMLToken&); + void handle_after_frameset(HTMLToken&); + void handle_after_after_frameset(HTMLToken&); + + void stop_parsing() { m_stop_parsing = true; } + + void generate_implied_end_tags(const FlyString& exception = {}); + void generate_all_implied_end_tags_thoroughly(); + bool stack_of_open_elements_has_element_with_tag_name_in_scope(const FlyString& tag_name); + NonnullRefPtr<DOM::Element> create_element_for(const HTMLToken&, const FlyString& namespace_); + + struct AdjustedInsertionLocation { + RefPtr<DOM::Node> parent; + RefPtr<DOM::Node> insert_before_sibling; + }; + + AdjustedInsertionLocation find_appropriate_place_for_inserting_node(); + + DOM::Text* find_character_insertion_node(); + void flush_character_insertions(); + RefPtr<DOM::Element> insert_foreign_element(const HTMLToken&, const FlyString&); + RefPtr<DOM::Element> insert_html_element(const HTMLToken&); + DOM::Element& current_node(); + DOM::Element& adjusted_current_node(); + DOM::Element& node_before_current_node(); + void insert_character(u32 data); + void insert_comment(HTMLToken&); + void reconstruct_the_active_formatting_elements(); + void close_a_p_element(); + void process_using_the_rules_for(InsertionMode, HTMLToken&); + void process_using_the_rules_for_foreign_content(HTMLToken&); + void parse_generic_raw_text_element(HTMLToken&); + void increment_script_nesting_level(); + void decrement_script_nesting_level(); + size_t script_nesting_level() const { return m_script_nesting_level; } + void reset_the_insertion_mode_appropriately(); + + void adjust_mathml_attributes(HTMLToken&); + void adjust_svg_tag_names(HTMLToken&); + void adjust_svg_attributes(HTMLToken&); + void adjust_foreign_attributes(HTMLToken&); + + enum AdoptionAgencyAlgorithmOutcome { + DoNothing, + RunAnyOtherEndTagSteps, + }; + + AdoptionAgencyAlgorithmOutcome run_the_adoption_agency_algorithm(HTMLToken&); + void clear_the_stack_back_to_a_table_context(); + void clear_the_stack_back_to_a_table_body_context(); + void clear_the_stack_back_to_a_table_row_context(); + void close_the_cell(); + + InsertionMode m_insertion_mode { InsertionMode::Initial }; + InsertionMode m_original_insertion_mode { InsertionMode::Initial }; + + StackOfOpenElements m_stack_of_open_elements; + Vector<InsertionMode> m_stack_of_template_insertion_modes; + ListOfActiveFormattingElements m_list_of_active_formatting_elements; + + HTMLTokenizer m_tokenizer; + + bool m_foster_parenting { false }; + bool m_frameset_ok { true }; + bool m_parsing_fragment { false }; + bool m_scripting_enabled { true }; + bool m_invoked_via_document_write { false }; + bool m_aborted { false }; + bool m_parser_pause_flag { false }; + bool m_stop_parsing { false }; + size_t m_script_nesting_level { 0 }; + + NonnullRefPtr<DOM::Document> m_document; + RefPtr<HTMLHeadElement> m_head_element; + RefPtr<HTMLFormElement> m_form_element; + RefPtr<DOM::Element> m_context_element; + + Vector<HTMLToken> m_pending_table_character_tokens; + + RefPtr<DOM::Text> m_character_insertion_node; + StringBuilder m_character_insertion_builder; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/Parser/HTMLToken.cpp b/Userland/Libraries/LibWeb/HTML/Parser/HTMLToken.cpp new file mode 100644 index 0000000000..53a2357be5 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Parser/HTMLToken.cpp @@ -0,0 +1,83 @@ +/* + * 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 <LibWeb/HTML/Parser/HTMLToken.h> + +namespace Web::HTML { + +String HTMLToken::to_string() const +{ + StringBuilder builder; + + switch (type()) { + case HTMLToken::Type::DOCTYPE: + builder.append("DOCTYPE"); + builder.append(" { name: '"); + builder.append(m_doctype.name.to_string()); + builder.append("' }"); + break; + case HTMLToken::Type::StartTag: + builder.append("StartTag"); + break; + case HTMLToken::Type::EndTag: + builder.append("EndTag"); + break; + case HTMLToken::Type::Comment: + builder.append("Comment"); + break; + case HTMLToken::Type::Character: + builder.append("Character"); + break; + case HTMLToken::Type::EndOfFile: + builder.append("EndOfFile"); + break; + case HTMLToken::Type::Invalid: + ASSERT_NOT_REACHED(); + } + + if (type() == HTMLToken::Type::StartTag || type() == HTMLToken::Type::EndTag) { + builder.append(" { name: '"); + builder.append(m_tag.tag_name.to_string()); + builder.append("', { "); + for (auto& attribute : m_tag.attributes) { + builder.append(attribute.local_name_builder.to_string()); + builder.append("=\""); + builder.append(attribute.value_builder.to_string()); + builder.append("\" "); + } + builder.append("} }"); + } + + if (type() == HTMLToken::Type::Comment || type() == HTMLToken::Type::Character) { + builder.append(" { data: '"); + builder.append(m_comment_or_character.data.to_string()); + builder.append("' }"); + } + + return builder.to_string(); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/Parser/HTMLToken.h b/Userland/Libraries/LibWeb/HTML/Parser/HTMLToken.h new file mode 100644 index 0000000000..c2246229c6 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Parser/HTMLToken.h @@ -0,0 +1,226 @@ +/* + * 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 <AK/FlyString.h> +#include <AK/String.h> +#include <AK/StringBuilder.h> +#include <AK/Types.h> +#include <AK/Utf8View.h> +#include <AK/Vector.h> + +namespace Web::HTML { + +class HTMLToken { + friend class HTMLDocumentParser; + friend class HTMLTokenizer; + +public: + enum class Type { + Invalid, + DOCTYPE, + StartTag, + EndTag, + Comment, + Character, + EndOfFile, + }; + + static HTMLToken make_character(u32 code_point) + { + HTMLToken token; + token.m_type = Type::Character; + token.m_comment_or_character.data.append(code_point); + return token; + } + + static HTMLToken make_start_tag(const FlyString& tag_name) + { + HTMLToken token; + token.m_type = Type::StartTag; + token.m_tag.tag_name.append(tag_name); + return token; + } + + bool is_doctype() const { return m_type == Type::DOCTYPE; } + bool is_start_tag() const { return m_type == Type::StartTag; } + bool is_end_tag() const { return m_type == Type::EndTag; } + bool is_comment() const { return m_type == Type::Comment; } + bool is_character() const { return m_type == Type::Character; } + bool is_end_of_file() const { return m_type == Type::EndOfFile; } + + u32 code_point() const + { + ASSERT(is_character()); + Utf8View view(m_comment_or_character.data.string_view()); + ASSERT(view.length() == 1); + return *view.begin(); + } + + bool is_parser_whitespace() const + { + // NOTE: The parser considers '\r' to be whitespace, while the tokenizer does not. + if (!is_character()) + return false; + switch (code_point()) { + case '\t': + case '\n': + case '\f': + case '\r': + case ' ': + return true; + default: + return false; + } + } + + String tag_name() const + { + ASSERT(is_start_tag() || is_end_tag()); + return m_tag.tag_name.to_string(); + } + + bool is_self_closing() const + { + ASSERT(is_start_tag() || is_end_tag()); + return m_tag.self_closing; + } + + bool has_acknowledged_self_closing_flag() const + { + ASSERT(is_self_closing()); + return m_tag.self_closing_acknowledged; + } + + void acknowledge_self_closing_flag_if_set() + { + if (is_self_closing()) + m_tag.self_closing_acknowledged = true; + } + + StringView attribute(const FlyString& attribute_name) + { + ASSERT(is_start_tag() || is_end_tag()); + for (auto& attribute : m_tag.attributes) { + if (attribute_name == attribute.local_name_builder.string_view()) + return attribute.value_builder.string_view(); + } + return {}; + } + + bool has_attribute(const FlyString& attribute_name) + { + return !attribute(attribute_name).is_null(); + } + + void adjust_tag_name(const FlyString& old_name, const FlyString& new_name) + { + ASSERT(is_start_tag() || is_end_tag()); + if (old_name == m_tag.tag_name.string_view()) { + m_tag.tag_name.clear(); + m_tag.tag_name.append(new_name); + } + } + + void adjust_attribute_name(const FlyString& old_name, const FlyString& new_name) + { + ASSERT(is_start_tag() || is_end_tag()); + for (auto& attribute : m_tag.attributes) { + if (old_name == attribute.local_name_builder.string_view()) { + attribute.local_name_builder.clear(); + attribute.local_name_builder.append(new_name); + } + } + } + + void adjust_foreign_attribute(const FlyString& old_name, const FlyString& prefix, const FlyString& local_name, const FlyString& namespace_) + { + ASSERT(is_start_tag() || is_end_tag()); + for (auto& attribute : m_tag.attributes) { + if (old_name == attribute.local_name_builder.string_view()) { + attribute.prefix_builder.clear(); + attribute.prefix_builder.append(prefix); + + attribute.local_name_builder.clear(); + attribute.local_name_builder.append(local_name); + + attribute.namespace_builder.clear(); + attribute.namespace_builder.append(namespace_); + } + } + } + + void drop_attributes() + { + ASSERT(is_start_tag() || is_end_tag()); + m_tag.attributes.clear(); + } + + Type type() const { return m_type; } + + String to_string() const; + +private: + struct AttributeBuilder { + StringBuilder prefix_builder; + StringBuilder local_name_builder; + StringBuilder namespace_builder; + StringBuilder value_builder; + }; + + Type m_type { Type::Invalid }; + + // Type::DOCTYPE + struct { + // NOTE: "Missing" is a distinct state from the empty string. + + StringBuilder name; + bool missing_name { true }; + StringBuilder public_identifier; + bool missing_public_identifier { true }; + StringBuilder system_identifier; + bool missing_system_identifier { true }; + bool force_quirks { false }; + } m_doctype; + + // Type::StartTag + // Type::EndTag + struct { + StringBuilder tag_name; + bool self_closing { false }; + bool self_closing_acknowledged { false }; + Vector<AttributeBuilder> attributes; + } m_tag; + + // Type::Comment + // Type::Character + struct { + StringBuilder data; + } m_comment_or_character; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/Parser/HTMLTokenizer.cpp b/Userland/Libraries/LibWeb/HTML/Parser/HTMLTokenizer.cpp new file mode 100644 index 0000000000..1bf1dab3c3 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Parser/HTMLTokenizer.cpp @@ -0,0 +1,2663 @@ +/* + * 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 <LibTextCodec/Decoder.h> +#include <LibWeb/HTML/Parser/Entities.h> +#include <LibWeb/HTML/Parser/HTMLToken.h> +#include <LibWeb/HTML/Parser/HTMLTokenizer.h> +#include <ctype.h> +#include <string.h> + +namespace Web::HTML { + +#pragma GCC diagnostic ignored "-Wunused-label" + +//#define TOKENIZER_TRACE + +#ifdef TOKENIZER_TRACE +# define PARSE_ERROR() \ + do { \ + dbg() << "Parse error (tokenization)" << __PRETTY_FUNCTION__ << " @ " << __LINE__; \ + } while (0) +#else +# define PARSE_ERROR() +#endif + +#define CONSUME_NEXT_INPUT_CHARACTER \ + current_input_character = next_code_point(); + +#define SWITCH_TO(new_state) \ + do { \ + will_switch_to(State::new_state); \ + m_state = State::new_state; \ + CONSUME_NEXT_INPUT_CHARACTER; \ + goto new_state; \ + } while (0) + +#define RECONSUME_IN(new_state) \ + do { \ + will_reconsume_in(State::new_state); \ + m_state = State::new_state; \ + goto new_state; \ + } while (0) + +#define SWITCH_TO_RETURN_STATE \ + do { \ + will_switch_to(m_return_state); \ + m_state = m_return_state; \ + goto _StartOfFunction; \ + } while (0) + +#define RECONSUME_IN_RETURN_STATE \ + do { \ + will_reconsume_in(m_return_state); \ + m_state = m_return_state; \ + if (current_input_character.has_value()) \ + m_utf8_iterator = m_prev_utf8_iterator; \ + goto _StartOfFunction; \ + } while (0) + +#define SWITCH_TO_AND_EMIT_CURRENT_TOKEN(new_state) \ + do { \ + will_switch_to(State::new_state); \ + m_state = State::new_state; \ + will_emit(m_current_token); \ + m_queued_tokens.enqueue(m_current_token); \ + return m_queued_tokens.dequeue(); \ + } while (0) + +#define EMIT_CHARACTER_AND_RECONSUME_IN(code_point, new_state) \ + do { \ + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); \ + will_reconsume_in(State::new_state); \ + m_state = State::new_state; \ + goto new_state; \ + } while (0) + +#define FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE \ + do { \ + for (auto code_point : m_temporary_buffer) { \ + if (consumed_as_part_of_an_attribute()) { \ + m_current_token.m_tag.attributes.last().value_builder.append_code_point(code_point); \ + } else { \ + create_new_token(HTMLToken::Type::Character); \ + m_current_token.m_comment_or_character.data.append_code_point(code_point); \ + m_queued_tokens.enqueue(m_current_token); \ + } \ + } \ + } while (0) + +#define DONT_CONSUME_NEXT_INPUT_CHARACTER \ + do { \ + m_utf8_iterator = m_prev_utf8_iterator; \ + } while (0) + +#define ON(code_point) \ + if (current_input_character.has_value() && current_input_character.value() == code_point) + +#define ON_EOF \ + if (!current_input_character.has_value()) + +#define ON_ASCII_ALPHA \ + if (current_input_character.has_value() && isalpha(current_input_character.value())) + +#define ON_ASCII_ALPHANUMERIC \ + if (current_input_character.has_value() && isalnum(current_input_character.value())) + +#define ON_ASCII_UPPER_ALPHA \ + if (current_input_character.has_value() && current_input_character.value() >= 'A' && current_input_character.value() <= 'Z') + +#define ON_ASCII_LOWER_ALPHA \ + if (current_input_character.has_value() && current_input_character.value() >= 'a' && current_input_character.value() <= 'z') + +#define ON_ASCII_DIGIT \ + if (current_input_character.has_value() && isdigit(current_input_character.value())) + +#define ON_ASCII_HEX_DIGIT \ + if (current_input_character.has_value() && isxdigit(current_input_character.value())) + +#define ON_WHITESPACE \ + if (current_input_character.has_value() && strchr("\t\n\f ", current_input_character.value())) + +#define ANYTHING_ELSE if (1) + +#define EMIT_EOF \ + do { \ + if (m_has_emitted_eof) \ + return {}; \ + m_has_emitted_eof = true; \ + create_new_token(HTMLToken::Type::EndOfFile); \ + will_emit(m_current_token); \ + m_queued_tokens.enqueue(m_current_token); \ + return m_queued_tokens.dequeue(); \ + } while (0) + +#define EMIT_CURRENT_TOKEN \ + do { \ + will_emit(m_current_token); \ + m_queued_tokens.enqueue(m_current_token); \ + return m_queued_tokens.dequeue(); \ + } while (0) + +#define EMIT_CHARACTER(code_point) \ + do { \ + create_new_token(HTMLToken::Type::Character); \ + m_current_token.m_comment_or_character.data.append_code_point(code_point); \ + m_queued_tokens.enqueue(m_current_token); \ + return m_queued_tokens.dequeue(); \ + } while (0) + +#define EMIT_CURRENT_CHARACTER \ + EMIT_CHARACTER(current_input_character.value()); + +#define SWITCH_TO_AND_EMIT_CHARACTER(code_point, new_state) \ + do { \ + will_switch_to(State::new_state); \ + m_state = State::new_state; \ + EMIT_CHARACTER(code_point); \ + } while (0) + +#define SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(new_state) \ + SWITCH_TO_AND_EMIT_CHARACTER(current_input_character.value(), new_state) + +#define BEGIN_STATE(state) \ + state: \ + case State::state: { \ + { \ + { + +#define END_STATE \ + ASSERT_NOT_REACHED(); \ + break; \ + } \ + } \ + } + +static inline bool is_surrogate(u32 code_point) +{ + return (code_point & 0xfffff800) == 0xd800; +} + +static inline bool is_noncharacter(u32 code_point) +{ + return code_point >= 0xfdd0 && (code_point <= 0xfdef || (code_point & 0xfffe) == 0xfffe) && code_point <= 0x10ffff; +} + +static inline bool is_c0_control(u32 code_point) +{ + return code_point <= 0x1f; +} + +static inline bool is_control(u32 code_point) +{ + return is_c0_control(code_point) || (code_point >= 0x7f && code_point <= 0x9f); +} + +Optional<u32> HTMLTokenizer::next_code_point() +{ + if (m_utf8_iterator == m_utf8_view.end()) + return {}; + m_prev_utf8_iterator = m_utf8_iterator; + ++m_utf8_iterator; +#ifdef TOKENIZER_TRACE + dbg() << "(Tokenizer) Next code_point: " << (char)*m_prev_utf8_iterator; +#endif + return *m_prev_utf8_iterator; +} + +Optional<u32> HTMLTokenizer::peek_code_point(size_t offset) const +{ + auto it = m_utf8_iterator; + for (size_t i = 0; i < offset && it != m_utf8_view.end(); ++i) + ++it; + if (it == m_utf8_view.end()) + return {}; + return *it; +} + +Optional<HTMLToken> HTMLTokenizer::next_token() +{ +_StartOfFunction: + if (!m_queued_tokens.is_empty()) + return m_queued_tokens.dequeue(); + + for (;;) { + auto current_input_character = next_code_point(); + switch (m_state) { + BEGIN_STATE(Data) + { + ON('&') + { + m_return_state = State::Data; + SWITCH_TO(CharacterReference); + } + ON('<') + { + SWITCH_TO(TagOpen); + } + ON(0) + { + PARSE_ERROR(); + EMIT_CURRENT_CHARACTER; + } + ON_EOF + { + EMIT_EOF; + } + ANYTHING_ELSE + { + EMIT_CURRENT_CHARACTER; + } + } + END_STATE + + BEGIN_STATE(TagOpen) + { + ON('!') + { + SWITCH_TO(MarkupDeclarationOpen); + } + ON('/') + { + SWITCH_TO(EndTagOpen); + } + ON_ASCII_ALPHA + { + create_new_token(HTMLToken::Type::StartTag); + RECONSUME_IN(TagName); + } + ON('?') + { + PARSE_ERROR(); + create_new_token(HTMLToken::Type::Comment); + RECONSUME_IN(BogusComment); + } + ON_EOF + { + PARSE_ERROR(); + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + EMIT_EOF; + } + ANYTHING_ELSE + { + PARSE_ERROR(); + EMIT_CHARACTER_AND_RECONSUME_IN('<', Data); + } + } + END_STATE + + BEGIN_STATE(TagName) + { + ON_WHITESPACE + { + SWITCH_TO(BeforeAttributeName); + } + ON('/') + { + SWITCH_TO(SelfClosingStartTag); + } + ON('>') + { + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_ASCII_UPPER_ALPHA + { + m_current_token.m_tag.tag_name.append(tolower(current_input_character.value())); + continue; + } + ON(0) + { + PARSE_ERROR(); + m_current_token.m_tag.tag_name.append_code_point(0xFFFD); + continue; + } + ON_EOF + { + PARSE_ERROR(); + EMIT_EOF; + } + ANYTHING_ELSE + { + m_current_token.m_tag.tag_name.append_code_point(current_input_character.value()); + continue; + } + } + END_STATE + + BEGIN_STATE(EndTagOpen) + { + ON_ASCII_ALPHA + { + create_new_token(HTMLToken::Type::EndTag); + RECONSUME_IN(TagName); + } + ON('>') + { + PARSE_ERROR(); + SWITCH_TO(Data); + } + ON_EOF + { + PARSE_ERROR(); + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + EMIT_EOF; + } + ANYTHING_ELSE + { + PARSE_ERROR(); + create_new_token(HTMLToken::Type::Comment); + RECONSUME_IN(BogusComment); + } + } + END_STATE + + BEGIN_STATE(MarkupDeclarationOpen) + { + DONT_CONSUME_NEXT_INPUT_CHARACTER; + if (consume_next_if_match("--")) { + create_new_token(HTMLToken::Type::Comment); + SWITCH_TO(CommentStart); + } + if (consume_next_if_match("DOCTYPE", CaseSensitivity::CaseInsensitive)) { + SWITCH_TO(DOCTYPE); + } + if (consume_next_if_match("[CDATA[")) { + TODO(); + } + ANYTHING_ELSE + { + PARSE_ERROR(); + create_new_token(HTMLToken::Type::Comment); + SWITCH_TO(BogusComment); + } + } + END_STATE + + BEGIN_STATE(BogusComment) + { + ON('>') + { + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ON(0) + { + PARSE_ERROR(); + m_current_token.m_comment_or_character.data.append_code_point(0xFFFD); + continue; + } + ANYTHING_ELSE + { + m_current_token.m_comment_or_character.data.append_code_point(current_input_character.value()); + continue; + } + } + END_STATE + + BEGIN_STATE(DOCTYPE) + { + ON_WHITESPACE + { + SWITCH_TO(BeforeDOCTYPEName); + } + ON('>') + { + RECONSUME_IN(BeforeDOCTYPEName); + } + ON_EOF + { + PARSE_ERROR(); + create_new_token(HTMLToken::Type::DOCTYPE); + m_current_token.m_doctype.force_quirks = true; + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + PARSE_ERROR(); + RECONSUME_IN(BeforeDOCTYPEName); + } + } + END_STATE + + BEGIN_STATE(BeforeDOCTYPEName) + { + ON_WHITESPACE + { + continue; + } + ON_ASCII_UPPER_ALPHA + { + create_new_token(HTMLToken::Type::DOCTYPE); + m_current_token.m_doctype.name.append(tolower(current_input_character.value())); + m_current_token.m_doctype.missing_name = false; + SWITCH_TO(DOCTYPEName); + } + ON(0) + { + PARSE_ERROR(); + create_new_token(HTMLToken::Type::DOCTYPE); + m_current_token.m_doctype.name.append_code_point(0xFFFD); + m_current_token.m_doctype.missing_name = false; + SWITCH_TO(DOCTYPEName); + } + ON('>') + { + PARSE_ERROR(); + create_new_token(HTMLToken::Type::DOCTYPE); + m_current_token.m_doctype.force_quirks = true; + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + create_new_token(HTMLToken::Type::DOCTYPE); + m_current_token.m_doctype.force_quirks = true; + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + create_new_token(HTMLToken::Type::DOCTYPE); + m_current_token.m_doctype.name.append_code_point(current_input_character.value()); + m_current_token.m_doctype.missing_name = false; + SWITCH_TO(DOCTYPEName); + } + } + END_STATE + + BEGIN_STATE(DOCTYPEName) + { + ON_WHITESPACE + { + SWITCH_TO(AfterDOCTYPEName); + } + ON('>') + { + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_ASCII_UPPER_ALPHA + { + m_current_token.m_doctype.name.append(tolower(current_input_character.value())); + continue; + } + ON(0) + { + PARSE_ERROR(); + m_current_token.m_doctype.name.append_code_point(0xFFFD); + continue; + } + ON_EOF + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + m_current_token.m_doctype.name.append_code_point(current_input_character.value()); + continue; + } + } + END_STATE + + BEGIN_STATE(AfterDOCTYPEName) + { + ON_WHITESPACE + { + continue; + } + ON('>') + { + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + if (toupper(current_input_character.value()) == 'P' && consume_next_if_match("UBLIC", CaseSensitivity::CaseInsensitive)) { + SWITCH_TO(AfterDOCTYPEPublicKeyword); + } + if (toupper(current_input_character.value()) == 'S' && consume_next_if_match("YSTEM", CaseSensitivity::CaseInsensitive)) { + SWITCH_TO(AfterDOCTYPESystemKeyword); + } + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + RECONSUME_IN(BogusDOCTYPE); + } + } + END_STATE + + BEGIN_STATE(AfterDOCTYPEPublicKeyword) + { + ON_WHITESPACE + { + SWITCH_TO(BeforeDOCTYPEPublicIdentifier); + } + ON('"') + { + PARSE_ERROR(); + m_current_token.m_doctype.public_identifier.clear(); + m_current_token.m_doctype.missing_public_identifier = false; + SWITCH_TO(DOCTYPEPublicIdentifierDoubleQuoted); + } + ON('\'') + { + PARSE_ERROR(); + m_current_token.m_doctype.public_identifier.clear(); + m_current_token.m_doctype.missing_public_identifier = false; + SWITCH_TO(DOCTYPEPublicIdentifierSingleQuoted); + } + ON('>') + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + RECONSUME_IN(BogusDOCTYPE); + } + } + END_STATE + + BEGIN_STATE(AfterDOCTYPESystemKeyword) + { + ON_WHITESPACE + { + SWITCH_TO(BeforeDOCTYPESystemIdentifier); + } + ON('"') + { + PARSE_ERROR(); + m_current_token.m_doctype.system_identifier.clear(); + m_current_token.m_doctype.missing_system_identifier = false; + SWITCH_TO(DOCTYPESystemIdentifierDoubleQuoted); + } + ON('\'') + { + PARSE_ERROR(); + m_current_token.m_doctype.system_identifier.clear(); + m_current_token.m_doctype.missing_system_identifier = false; + SWITCH_TO(DOCTYPESystemIdentifierSingleQuoted); + } + ON('>') + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + RECONSUME_IN(BogusDOCTYPE); + } + } + END_STATE + + BEGIN_STATE(BeforeDOCTYPEPublicIdentifier) + { + ON_WHITESPACE + { + continue; + } + ON('"') + { + m_current_token.m_doctype.public_identifier.clear(); + m_current_token.m_doctype.missing_public_identifier = false; + SWITCH_TO(DOCTYPEPublicIdentifierDoubleQuoted); + } + ON('\'') + { + m_current_token.m_doctype.public_identifier.clear(); + m_current_token.m_doctype.missing_public_identifier = false; + SWITCH_TO(DOCTYPEPublicIdentifierSingleQuoted); + } + ON('>') + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + RECONSUME_IN(BogusDOCTYPE); + } + } + END_STATE + + BEGIN_STATE(BeforeDOCTYPESystemIdentifier) + { + ON_WHITESPACE + { + continue; + } + ON('"') + { + m_current_token.m_doctype.system_identifier.clear(); + m_current_token.m_doctype.missing_system_identifier = false; + SWITCH_TO(DOCTYPESystemIdentifierDoubleQuoted); + } + ON('\'') + { + m_current_token.m_doctype.system_identifier.clear(); + m_current_token.m_doctype.missing_system_identifier = false; + SWITCH_TO(DOCTYPESystemIdentifierSingleQuoted); + } + ON('>') + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + RECONSUME_IN(BogusDOCTYPE); + } + } + END_STATE + + BEGIN_STATE(DOCTYPEPublicIdentifierDoubleQuoted) + { + ON('"') + { + SWITCH_TO(AfterDOCTYPEPublicIdentifier); + } + ON(0) + { + PARSE_ERROR(); + m_current_token.m_doctype.public_identifier.append_code_point(0xFFFD); + continue; + } + ON('>') + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + m_current_token.m_doctype.public_identifier.append_code_point(current_input_character.value()); + continue; + } + } + END_STATE + + BEGIN_STATE(DOCTYPEPublicIdentifierSingleQuoted) + { + ON('\'') + { + SWITCH_TO(AfterDOCTYPEPublicIdentifier); + } + ON(0) + { + PARSE_ERROR(); + m_current_token.m_doctype.public_identifier.append_code_point(0xFFFD); + continue; + } + ON('>') + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + m_current_token.m_doctype.public_identifier.append_code_point(current_input_character.value()); + continue; + } + } + END_STATE + + BEGIN_STATE(DOCTYPESystemIdentifierDoubleQuoted) + { + ON('"') + { + SWITCH_TO(AfterDOCTYPESystemIdentifier); + } + ON(0) + { + PARSE_ERROR(); + m_current_token.m_doctype.system_identifier.append_code_point(0xFFFD); + continue; + } + ON('>') + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + m_current_token.m_doctype.system_identifier.append_code_point(current_input_character.value()); + continue; + } + } + END_STATE + + BEGIN_STATE(DOCTYPESystemIdentifierSingleQuoted) + { + ON('\'') + { + SWITCH_TO(AfterDOCTYPESystemIdentifier); + } + ON(0) + { + PARSE_ERROR(); + m_current_token.m_doctype.system_identifier.append_code_point(0xFFFD); + continue; + } + ON('>') + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + m_current_token.m_doctype.system_identifier.append_code_point(current_input_character.value()); + continue; + } + } + END_STATE + + BEGIN_STATE(AfterDOCTYPEPublicIdentifier) + { + ON_WHITESPACE + { + SWITCH_TO(BetweenDOCTYPEPublicAndSystemIdentifiers); + } + ON('>') + { + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON('"') + { + PARSE_ERROR(); + m_current_token.m_doctype.system_identifier.clear(); + m_current_token.m_doctype.missing_system_identifier = false; + SWITCH_TO(DOCTYPESystemIdentifierDoubleQuoted); + } + ON('\'') + { + PARSE_ERROR(); + m_current_token.m_doctype.system_identifier.clear(); + m_current_token.m_doctype.missing_system_identifier = false; + SWITCH_TO(DOCTYPESystemIdentifierSingleQuoted); + } + ON_EOF + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + RECONSUME_IN(BogusDOCTYPE); + } + } + END_STATE + + BEGIN_STATE(BetweenDOCTYPEPublicAndSystemIdentifiers) + { + ON_WHITESPACE + { + continue; + } + ON('>') + { + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON('"') + { + m_current_token.m_doctype.system_identifier.clear(); + m_current_token.m_doctype.missing_system_identifier = false; + SWITCH_TO(DOCTYPESystemIdentifierDoubleQuoted); + } + ON('\'') + { + m_current_token.m_doctype.system_identifier.clear(); + m_current_token.m_doctype.missing_system_identifier = false; + SWITCH_TO(DOCTYPESystemIdentifierSingleQuoted); + } + ON_EOF + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + RECONSUME_IN(BogusDOCTYPE); + } + } + END_STATE + + BEGIN_STATE(AfterDOCTYPESystemIdentifier) + { + ON_WHITESPACE + { + continue; + } + ON('>') + { + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + m_current_token.m_doctype.force_quirks = true; + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + PARSE_ERROR(); + RECONSUME_IN(BogusDOCTYPE); + } + } + END_STATE + + BEGIN_STATE(BogusDOCTYPE) + { + ON('>') + { + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON(0) + { + PARSE_ERROR(); + continue; + } + ON_EOF + { + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + continue; + } + } + END_STATE + + BEGIN_STATE(BeforeAttributeName) + { + ON_WHITESPACE + { + continue; + } + ON('/') + { + RECONSUME_IN(AfterAttributeName); + } + ON('>') + { + RECONSUME_IN(AfterAttributeName); + } + ON_EOF + { + RECONSUME_IN(AfterAttributeName); + } + ON('=') + { + PARSE_ERROR(); + auto new_attribute = HTMLToken::AttributeBuilder(); + new_attribute.local_name_builder.append_code_point(current_input_character.value()); + m_current_token.m_tag.attributes.append(new_attribute); + SWITCH_TO(AttributeName); + } + ANYTHING_ELSE + { + m_current_token.m_tag.attributes.append(HTMLToken::AttributeBuilder()); + RECONSUME_IN(AttributeName); + } + } + END_STATE + + BEGIN_STATE(SelfClosingStartTag) + { + ON('>') + { + m_current_token.m_tag.self_closing = true; + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + EMIT_EOF; + } + ANYTHING_ELSE + { + PARSE_ERROR(); + RECONSUME_IN(BeforeAttributeName); + } + } + END_STATE + + BEGIN_STATE(AttributeName) + { + ON_WHITESPACE + { + RECONSUME_IN(AfterAttributeName); + } + ON('/') + { + RECONSUME_IN(AfterAttributeName); + } + ON('>') + { + RECONSUME_IN(AfterAttributeName); + } + ON_EOF + { + RECONSUME_IN(AfterAttributeName); + } + ON('=') + { + SWITCH_TO(BeforeAttributeValue); + } + ON_ASCII_UPPER_ALPHA + { + m_current_token.m_tag.attributes.last().local_name_builder.append_code_point(tolower(current_input_character.value())); + continue; + } + ON(0) + { + PARSE_ERROR(); + m_current_token.m_tag.attributes.last().local_name_builder.append_code_point(0xFFFD); + continue; + } + ON('"') + { + PARSE_ERROR(); + goto AnythingElseAttributeName; + } + ON('\'') + { + PARSE_ERROR(); + goto AnythingElseAttributeName; + } + ON('<') + { + PARSE_ERROR(); + goto AnythingElseAttributeName; + } + ANYTHING_ELSE + { + AnythingElseAttributeName: + m_current_token.m_tag.attributes.last().local_name_builder.append_code_point(current_input_character.value()); + continue; + } + } + END_STATE + + BEGIN_STATE(AfterAttributeName) + { + ON_WHITESPACE + { + continue; + } + ON('/') + { + SWITCH_TO(SelfClosingStartTag); + } + ON('=') + { + SWITCH_TO(BeforeAttributeValue); + } + ON('>') + { + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + EMIT_EOF; + } + ANYTHING_ELSE + { + m_current_token.m_tag.attributes.append(HTMLToken::AttributeBuilder()); + RECONSUME_IN(AttributeName); + } + } + END_STATE + + BEGIN_STATE(BeforeAttributeValue) + { + ON_WHITESPACE + { + continue; + } + ON('"') + { + SWITCH_TO(AttributeValueDoubleQuoted); + } + ON('\'') + { + SWITCH_TO(AttributeValueSingleQuoted); + } + ON('>') + { + PARSE_ERROR(); + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ANYTHING_ELSE + { + RECONSUME_IN(AttributeValueUnquoted); + } + } + END_STATE + + BEGIN_STATE(AttributeValueDoubleQuoted) + { + ON('"') + { + SWITCH_TO(AfterAttributeValueQuoted); + } + ON('&') + { + m_return_state = State::AttributeValueDoubleQuoted; + SWITCH_TO(CharacterReference); + } + ON(0) + { + PARSE_ERROR(); + m_current_token.m_tag.attributes.last().value_builder.append_code_point(0xFFFD); + continue; + } + ON_EOF + { + PARSE_ERROR(); + EMIT_EOF; + } + ANYTHING_ELSE + { + m_current_token.m_tag.attributes.last().value_builder.append_code_point(current_input_character.value()); + continue; + } + } + END_STATE + + BEGIN_STATE(AttributeValueSingleQuoted) + { + ON('\'') + { + SWITCH_TO(AfterAttributeValueQuoted); + } + ON('&') + { + m_return_state = State::AttributeValueSingleQuoted; + SWITCH_TO(CharacterReference); + } + ON(0) + { + PARSE_ERROR(); + m_current_token.m_tag.attributes.last().value_builder.append_code_point(0xFFFD); + continue; + } + ON_EOF + { + PARSE_ERROR(); + EMIT_EOF; + } + ANYTHING_ELSE + { + m_current_token.m_tag.attributes.last().value_builder.append_code_point(current_input_character.value()); + continue; + } + } + END_STATE + + BEGIN_STATE(AttributeValueUnquoted) + { + ON_WHITESPACE + { + SWITCH_TO(BeforeAttributeName); + } + ON('&') + { + m_return_state = State::AttributeValueUnquoted; + SWITCH_TO(CharacterReference); + } + ON('>') + { + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON(0) + { + PARSE_ERROR(); + m_current_token.m_tag.attributes.last().value_builder.append_code_point(0xFFFD); + continue; + } + ON('"') + { + PARSE_ERROR(); + goto AnythingElseAttributeValueUnquoted; + } + ON('\'') + { + PARSE_ERROR(); + goto AnythingElseAttributeValueUnquoted; + } + ON('<') + { + PARSE_ERROR(); + goto AnythingElseAttributeValueUnquoted; + } + ON('=') + { + PARSE_ERROR(); + goto AnythingElseAttributeValueUnquoted; + } + ON('`') + { + PARSE_ERROR(); + goto AnythingElseAttributeValueUnquoted; + } + ON_EOF + { + PARSE_ERROR(); + EMIT_EOF; + } + ANYTHING_ELSE + { + AnythingElseAttributeValueUnquoted: + m_current_token.m_tag.attributes.last().value_builder.append_code_point(current_input_character.value()); + continue; + } + } + END_STATE + + BEGIN_STATE(AfterAttributeValueQuoted) + { + ON_WHITESPACE + { + SWITCH_TO(BeforeAttributeName); + } + ON('/') + { + SWITCH_TO(SelfClosingStartTag); + } + ON('>') + { + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + EMIT_EOF; + } + ANYTHING_ELSE + { + PARSE_ERROR(); + RECONSUME_IN(BeforeAttributeName); + } + } + END_STATE + + BEGIN_STATE(CommentStart) + { + ON('-') + { + SWITCH_TO(CommentStartDash); + } + ON('>') + { + PARSE_ERROR(); + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ANYTHING_ELSE + { + RECONSUME_IN(Comment); + } + } + END_STATE + + BEGIN_STATE(CommentStartDash) + { + ON('-') + { + SWITCH_TO(CommentEnd); + } + ON('>') + { + PARSE_ERROR(); + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + m_current_token.m_comment_or_character.data.append('-'); + RECONSUME_IN(Comment); + } + } + END_STATE + + BEGIN_STATE(Comment) + { + ON('<') + { + m_current_token.m_comment_or_character.data.append_code_point(current_input_character.value()); + SWITCH_TO(CommentLessThanSign); + } + ON('-') + { + SWITCH_TO(CommentEndDash); + } + ON(0) + { + PARSE_ERROR(); + m_current_token.m_comment_or_character.data.append_code_point(0xFFFD); + continue; + } + ON_EOF + { + PARSE_ERROR(); + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + m_current_token.m_comment_or_character.data.append_code_point(current_input_character.value()); + continue; + } + } + END_STATE + + BEGIN_STATE(CommentEnd) + { + ON('>') + { + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON('!') + { + SWITCH_TO(CommentEndBang); + } + ON('-') + { + m_current_token.m_comment_or_character.data.append('-'); + continue; + } + ON_EOF + { + PARSE_ERROR(); + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + m_current_token.m_comment_or_character.data.append('-'); + RECONSUME_IN(Comment); + } + } + END_STATE + + BEGIN_STATE(CommentEndBang) + { + ON('-') + { + m_current_token.m_comment_or_character.data.append("--!"); + SWITCH_TO(CommentEndDash); + } + ON('>') + { + PARSE_ERROR(); + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_EOF + { + PARSE_ERROR(); + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + m_current_token.m_comment_or_character.data.append("--!"); + RECONSUME_IN(Comment); + } + } + END_STATE + + BEGIN_STATE(CommentEndDash) + { + ON('-') + { + SWITCH_TO(CommentEnd); + } + ON_EOF + { + PARSE_ERROR(); + m_queued_tokens.enqueue(m_current_token); + EMIT_EOF; + } + ANYTHING_ELSE + { + m_current_token.m_comment_or_character.data.append('-'); + RECONSUME_IN(Comment); + } + } + END_STATE + + BEGIN_STATE(CommentLessThanSign) + { + ON('!') + { + m_current_token.m_comment_or_character.data.append_code_point(current_input_character.value()); + SWITCH_TO(CommentLessThanSignBang); + } + ON('<') + { + m_current_token.m_comment_or_character.data.append_code_point(current_input_character.value()); + continue; + } + ANYTHING_ELSE + { + RECONSUME_IN(Comment); + } + } + END_STATE + + BEGIN_STATE(CommentLessThanSignBang) + { + ON('-') + { + SWITCH_TO(CommentLessThanSignBangDash); + } + ANYTHING_ELSE + { + RECONSUME_IN(Comment); + } + } + END_STATE + + BEGIN_STATE(CommentLessThanSignBangDash) + { + ON('-') + { + SWITCH_TO(CommentLessThanSignBangDashDash); + } + ANYTHING_ELSE + { + RECONSUME_IN(CommentEndDash); + } + } + END_STATE + + BEGIN_STATE(CommentLessThanSignBangDashDash) + { + ON('>') + { + RECONSUME_IN(CommentEnd); + } + ON_EOF + { + RECONSUME_IN(CommentEnd); + } + ANYTHING_ELSE + { + PARSE_ERROR(); + RECONSUME_IN(CommentEnd); + } + } + END_STATE + + BEGIN_STATE(CharacterReference) + { + m_temporary_buffer.clear(); + m_temporary_buffer.append('&'); + + ON_ASCII_ALPHANUMERIC + { + RECONSUME_IN(NamedCharacterReference); + } + ON('#') + { + m_temporary_buffer.append(current_input_character.value()); + SWITCH_TO(NumericCharacterReference); + } + ANYTHING_ELSE + { + FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE; + RECONSUME_IN_RETURN_STATE; + } + } + END_STATE + + BEGIN_STATE(NamedCharacterReference) + { + size_t byte_offset = m_utf8_view.byte_offset_of(m_prev_utf8_iterator); + + auto match = HTML::code_points_from_entity(m_decoded_input.substring_view(byte_offset, m_decoded_input.length() - byte_offset - 1)); + + if (match.has_value()) { + for (size_t i = 0; i < match.value().entity.length() - 1; ++i) { + m_prev_utf8_iterator = m_utf8_iterator; + ++m_utf8_iterator; + } + for (auto ch : match.value().entity) + m_temporary_buffer.append(ch); + + if (consumed_as_part_of_an_attribute() && !match.value().entity.ends_with(';')) { + auto next_code_point = peek_code_point(0); + if (next_code_point.has_value() && (next_code_point.value() == '=' || isalnum(next_code_point.value()))) { + FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE; + SWITCH_TO_RETURN_STATE; + } + } + + if (!match.value().entity.ends_with(';')) { + PARSE_ERROR(); + } + + m_temporary_buffer.clear(); + m_temporary_buffer.append(match.value().code_points); + + FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE; + SWITCH_TO_RETURN_STATE; + } else { + FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE; + // FIXME: This should be SWITCH_TO, but we always lose the first character on this path, so just reconsume it. + // I can't wrap my head around how to do it as the spec says. + RECONSUME_IN(AmbiguousAmpersand); + } + } + END_STATE + + BEGIN_STATE(AmbiguousAmpersand) + { + ON_ASCII_ALPHANUMERIC + { + if (consumed_as_part_of_an_attribute()) { + m_current_token.m_tag.attributes.last().value_builder.append_code_point(current_input_character.value()); + continue; + } else { + EMIT_CURRENT_CHARACTER; + } + } + ON(';') + { + PARSE_ERROR(); + RECONSUME_IN_RETURN_STATE; + } + ANYTHING_ELSE + { + RECONSUME_IN_RETURN_STATE; + } + } + END_STATE + + BEGIN_STATE(NumericCharacterReference) + { + m_character_reference_code = 0; + + ON('X') + { + m_temporary_buffer.append(current_input_character.value()); + SWITCH_TO(HexadecimalCharacterReferenceStart); + } + ON('x') + { + m_temporary_buffer.append(current_input_character.value()); + SWITCH_TO(HexadecimalCharacterReferenceStart); + } + ANYTHING_ELSE + { + RECONSUME_IN(DecimalCharacterReferenceStart); + } + } + END_STATE + + BEGIN_STATE(HexadecimalCharacterReferenceStart) + { + ON_ASCII_HEX_DIGIT + { + RECONSUME_IN(HexadecimalCharacterReference); + } + ANYTHING_ELSE + { + PARSE_ERROR(); + FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE; + RECONSUME_IN_RETURN_STATE; + } + } + END_STATE + + BEGIN_STATE(DecimalCharacterReferenceStart) + { + ON_ASCII_DIGIT + { + RECONSUME_IN(DecimalCharacterReference); + } + ANYTHING_ELSE + { + PARSE_ERROR(); + FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE; + RECONSUME_IN_RETURN_STATE; + } + } + END_STATE + + BEGIN_STATE(HexadecimalCharacterReference) + { + ON_ASCII_DIGIT + { + m_character_reference_code *= 16; + m_character_reference_code += current_input_character.value() - 0x30; + continue; + } + ON_ASCII_UPPER_ALPHA + { + m_character_reference_code *= 16; + m_character_reference_code += current_input_character.value() - 0x37; + continue; + } + ON_ASCII_LOWER_ALPHA + { + m_character_reference_code *= 16; + m_character_reference_code += current_input_character.value() - 0x57; + continue; + } + ON(';') + { + SWITCH_TO(NumericCharacterReferenceEnd); + } + ANYTHING_ELSE + { + PARSE_ERROR(); + RECONSUME_IN(NumericCharacterReferenceEnd); + } + } + END_STATE + + BEGIN_STATE(DecimalCharacterReference) + { + ON_ASCII_DIGIT + { + m_character_reference_code *= 10; + m_character_reference_code += current_input_character.value() - 0x30; + continue; + } + ON(';') + { + SWITCH_TO(NumericCharacterReferenceEnd); + } + ANYTHING_ELSE + { + PARSE_ERROR(); + RECONSUME_IN(NumericCharacterReferenceEnd); + } + } + END_STATE + + BEGIN_STATE(NumericCharacterReferenceEnd) + { + DONT_CONSUME_NEXT_INPUT_CHARACTER; + + if (m_character_reference_code == 0) { + PARSE_ERROR(); + m_character_reference_code = 0xFFFD; + } + if (m_character_reference_code > 0x10ffff) { + PARSE_ERROR(); + m_character_reference_code = 0xFFFD; + } + if (is_surrogate(m_character_reference_code)) { + PARSE_ERROR(); + m_character_reference_code = 0xFFFD; + } + if (is_noncharacter(m_character_reference_code)) { + PARSE_ERROR(); + } + if (m_character_reference_code == 0xd || (is_control(m_character_reference_code) && !isspace(m_character_reference_code))) { + PARSE_ERROR(); + constexpr struct { + u32 number; + u32 code_point; + } conversion_table[] = { + { 0x80, 0x20AC }, + { 0x82, 0x201A }, + { 0x83, 0x0192 }, + { 0x84, 0x201E }, + { 0x85, 0x2026 }, + { 0x86, 0x2020 }, + { 0x87, 0x2021 }, + { 0x88, 0x02C6 }, + { 0x89, 0x2030 }, + { 0x8A, 0x0160 }, + { 0x8B, 0x2039 }, + { 0x8C, 0x0152 }, + { 0x8E, 0x017D }, + { 0x91, 0x2018 }, + { 0x92, 0x2019 }, + { 0x93, 0x201C }, + { 0x94, 0x201D }, + { 0x95, 0x2022 }, + { 0x96, 0x2013 }, + { 0x97, 0x2014 }, + { 0x98, 0x02DC }, + { 0x99, 0x2122 }, + { 0x9A, 0x0161 }, + { 0x9B, 0x203A }, + { 0x9C, 0x0153 }, + { 0x9E, 0x017E }, + { 0x9F, 0x0178 }, + }; + for (auto& entry : conversion_table) { + if (m_character_reference_code == entry.number) { + m_character_reference_code = entry.code_point; + break; + } + } + } + + m_temporary_buffer.clear(); + m_temporary_buffer.append(m_character_reference_code); + FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE; + SWITCH_TO_RETURN_STATE; + } + END_STATE + + BEGIN_STATE(RCDATA) + { + ON('&') + { + m_return_state = State::RCDATA; + SWITCH_TO(CharacterReference); + } + ON('<') + { + SWITCH_TO(RCDATALessThanSign); + } + ON(0) + { + PARSE_ERROR(); + EMIT_CHARACTER(0xFFFD); + } + ON_EOF + { + EMIT_EOF; + } + ANYTHING_ELSE + { + EMIT_CURRENT_CHARACTER; + } + } + END_STATE + + BEGIN_STATE(RCDATALessThanSign) + { + ON('/') + { + m_temporary_buffer.clear(); + SWITCH_TO(RCDATAEndTagOpen); + } + ANYTHING_ELSE + { + EMIT_CHARACTER_AND_RECONSUME_IN('<', RCDATA); + } + } + END_STATE + + BEGIN_STATE(RCDATAEndTagOpen) + { + ON_ASCII_ALPHA + { + create_new_token(HTMLToken::Type::EndTag); + RECONSUME_IN(RCDATAEndTagName); + } + ANYTHING_ELSE + { + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + RECONSUME_IN(RCDATA); + } + } + END_STATE + + BEGIN_STATE(RCDATAEndTagName) + { + ON_WHITESPACE + { + if (!current_end_tag_token_is_appropriate()) { + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + RECONSUME_IN(RCDATA); + } + SWITCH_TO(BeforeAttributeName); + } + ON('/') + { + if (!current_end_tag_token_is_appropriate()) { + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + RECONSUME_IN(RCDATA); + } + SWITCH_TO(SelfClosingStartTag); + } + ON('>') + { + if (!current_end_tag_token_is_appropriate()) { + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + RECONSUME_IN(RCDATA); + } + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_ASCII_UPPER_ALPHA + { + m_current_token.m_tag.tag_name.append(tolower(current_input_character.value())); + m_temporary_buffer.append(current_input_character.value()); + continue; + } + ON_ASCII_LOWER_ALPHA + { + m_current_token.m_tag.tag_name.append_code_point(current_input_character.value()); + m_temporary_buffer.append(current_input_character.value()); + continue; + } + ANYTHING_ELSE + { + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + RECONSUME_IN(RCDATA); + } + } + END_STATE + + BEGIN_STATE(RAWTEXT) + { + ON('<') + { + SWITCH_TO(RAWTEXTLessThanSign); + } + ON(0) + { + PARSE_ERROR(); + EMIT_CHARACTER(0xFFFD); + } + ON_EOF + { + EMIT_EOF; + } + ANYTHING_ELSE + { + EMIT_CURRENT_CHARACTER; + } + } + END_STATE + + BEGIN_STATE(RAWTEXTLessThanSign) + { + ON('/') + { + m_temporary_buffer.clear(); + SWITCH_TO(RAWTEXTEndTagOpen); + } + ANYTHING_ELSE + { + EMIT_CHARACTER_AND_RECONSUME_IN('<', RAWTEXT); + } + } + END_STATE + + BEGIN_STATE(RAWTEXTEndTagOpen) + { + ON_ASCII_ALPHA + { + create_new_token(HTMLToken::Type::EndTag); + RECONSUME_IN(RAWTEXTEndTagName); + } + ANYTHING_ELSE + { + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + RECONSUME_IN(RAWTEXT); + } + } + END_STATE + + BEGIN_STATE(RAWTEXTEndTagName) + { + ON_WHITESPACE + { + if (!current_end_tag_token_is_appropriate()) { + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + RECONSUME_IN(RAWTEXT); + } + SWITCH_TO(BeforeAttributeName); + } + ON('/') + { + if (!current_end_tag_token_is_appropriate()) { + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + RECONSUME_IN(RAWTEXT); + } + SWITCH_TO(SelfClosingStartTag); + } + ON('>') + { + if (!current_end_tag_token_is_appropriate()) { + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + RECONSUME_IN(RAWTEXT); + } + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + } + ON_ASCII_UPPER_ALPHA + { + m_current_token.m_tag.tag_name.append(tolower(current_input_character.value())); + m_temporary_buffer.append(current_input_character.value()); + continue; + } + ON_ASCII_LOWER_ALPHA + { + m_current_token.m_tag.tag_name.append(current_input_character.value()); + m_temporary_buffer.append(current_input_character.value()); + continue; + } + ANYTHING_ELSE + { + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + RECONSUME_IN(RAWTEXT); + } + } + END_STATE + + BEGIN_STATE(ScriptData) + { + ON('<') + { + SWITCH_TO(ScriptDataLessThanSign); + } + ON(0) + { + PARSE_ERROR(); + EMIT_CHARACTER(0xFFFD); + } + ON_EOF + { + EMIT_EOF; + } + ANYTHING_ELSE + { + EMIT_CURRENT_CHARACTER; + } + } + END_STATE + + BEGIN_STATE(PLAINTEXT) + { + ON(0) + { + PARSE_ERROR(); + EMIT_CHARACTER(0xFFFD); + } + ON_EOF + { + EMIT_EOF; + } + ANYTHING_ELSE + { + EMIT_CURRENT_CHARACTER; + } + } + END_STATE + + BEGIN_STATE(ScriptDataLessThanSign) + { + ON('/') + { + m_temporary_buffer.clear(); + SWITCH_TO(ScriptDataEndTagOpen); + } + ON('!') + { + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('!')); + SWITCH_TO(ScriptDataEscapeStart); + } + ANYTHING_ELSE + { + EMIT_CHARACTER_AND_RECONSUME_IN('<', ScriptData); + } + } + END_STATE + + BEGIN_STATE(ScriptDataEscapeStart) + { + ON('-') + { + SWITCH_TO_AND_EMIT_CHARACTER('-', ScriptDataEscapeStartDash); + } + ANYTHING_ELSE + { + RECONSUME_IN(ScriptData); + } + } + END_STATE + + BEGIN_STATE(ScriptDataEscapeStartDash) + { + ON('-') + { + SWITCH_TO_AND_EMIT_CHARACTER('-', ScriptDataEscapedDashDash); + } + ANYTHING_ELSE + { + RECONSUME_IN(ScriptData); + } + } + END_STATE + + BEGIN_STATE(ScriptDataEscapedDashDash) + { + ON('-') + { + EMIT_CHARACTER('-'); + } + ON('<') + { + SWITCH_TO(ScriptDataEscapedLessThanSign); + } + ON('>') + { + SWITCH_TO_AND_EMIT_CHARACTER('>', ScriptData); + } + ON(0) + { + PARSE_ERROR(); + SWITCH_TO_AND_EMIT_CHARACTER(0xFFFD, ScriptDataEscaped); + } + ON_EOF + { + PARSE_ERROR(); + EMIT_EOF; + } + ANYTHING_ELSE + { + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped); + } + } + END_STATE + + BEGIN_STATE(ScriptDataEscapedLessThanSign) + { + ON('/') + { + m_temporary_buffer.clear(); + SWITCH_TO(ScriptDataEscapedEndTagOpen); + } + ON_ASCII_ALPHA + { + m_temporary_buffer.clear(); + EMIT_CHARACTER_AND_RECONSUME_IN('<', ScriptDataDoubleEscapeStart); + } + ANYTHING_ELSE + { + EMIT_CHARACTER_AND_RECONSUME_IN('<', ScriptDataEscaped); + } + } + END_STATE + + BEGIN_STATE(ScriptDataEscapedEndTagOpen) + { + ON_ASCII_ALPHA + { + create_new_token(HTMLToken::Type::EndTag); + RECONSUME_IN(ScriptDataEscapedEndTagName); + } + ANYTHING_ELSE + { + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + RECONSUME_IN(ScriptDataEscaped); + } + } + END_STATE + + BEGIN_STATE(ScriptDataEscapedEndTagName) + { + ON_WHITESPACE + { + if (current_end_tag_token_is_appropriate()) + SWITCH_TO(BeforeAttributeName); + + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) { + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + } + RECONSUME_IN(ScriptDataEscaped); + } + ON('/') + { + if (current_end_tag_token_is_appropriate()) + SWITCH_TO(SelfClosingStartTag); + + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) { + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + } + RECONSUME_IN(ScriptDataEscaped); + } + ON('>') + { + if (current_end_tag_token_is_appropriate()) + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) { + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + } + RECONSUME_IN(ScriptDataEscaped); + } + ON_ASCII_UPPER_ALPHA + { + m_current_token.m_tag.tag_name.append(tolower(current_input_character.value())); + m_temporary_buffer.append(current_input_character.value()); + continue; + } + ON_ASCII_LOWER_ALPHA + { + m_current_token.m_tag.tag_name.append(current_input_character.value()); + m_temporary_buffer.append(current_input_character.value()); + continue; + } + ANYTHING_ELSE + { + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) { + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + } + RECONSUME_IN(ScriptDataEscaped); + } + } + END_STATE + + BEGIN_STATE(ScriptDataDoubleEscapeStart) + { + auto temporary_buffer_equal_to_script = [this]() -> bool { + if (m_temporary_buffer.size() != 6) + return false; + + // FIXME: Is there a better way of doing this? + return m_temporary_buffer[0] == 's' && m_temporary_buffer[1] == 'c' && m_temporary_buffer[2] == 'r' && m_temporary_buffer[3] == 'i' && m_temporary_buffer[4] == 'p' && m_temporary_buffer[5] == 't'; + }; + ON_WHITESPACE + { + if (temporary_buffer_equal_to_script()) + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped); + else + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped); + } + ON('/') + { + if (temporary_buffer_equal_to_script()) + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped); + else + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped); + } + ON('>') + { + if (temporary_buffer_equal_to_script()) + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped); + else + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped); + } + ON_ASCII_UPPER_ALPHA + { + m_temporary_buffer.append(tolower(current_input_character.value())); + EMIT_CURRENT_CHARACTER; + } + ON_ASCII_LOWER_ALPHA + { + m_temporary_buffer.append(current_input_character.value()); + EMIT_CURRENT_CHARACTER; + } + ANYTHING_ELSE + { + RECONSUME_IN(ScriptDataEscaped); + } + } + END_STATE + + BEGIN_STATE(ScriptDataDoubleEscaped) + { + ON('-') + { + SWITCH_TO_AND_EMIT_CHARACTER('-', ScriptDataDoubleEscapedDash); + } + ON('<') + { + SWITCH_TO_AND_EMIT_CHARACTER('<', ScriptDataDoubleEscapedLessThanSign); + } + ON(0) + { + PARSE_ERROR(); + EMIT_CHARACTER(0xFFFD); + } + ON_EOF + { + PARSE_ERROR(); + EMIT_EOF; + } + ANYTHING_ELSE + { + EMIT_CURRENT_CHARACTER; + } + } + END_STATE + + BEGIN_STATE(ScriptDataDoubleEscapedDash) + { + ON('-') + { + SWITCH_TO_AND_EMIT_CHARACTER('-', ScriptDataDoubleEscapedDashDash); + } + ON('<') + { + SWITCH_TO_AND_EMIT_CHARACTER('<', ScriptDataDoubleEscapedLessThanSign); + } + ON(0) + { + PARSE_ERROR(); + SWITCH_TO_AND_EMIT_CHARACTER(0xFFFD, ScriptDataDoubleEscaped); + } + ON_EOF + { + PARSE_ERROR(); + EMIT_EOF; + } + ANYTHING_ELSE + { + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped); + } + } + END_STATE + + BEGIN_STATE(ScriptDataDoubleEscapedDashDash) + { + ON('-') + { + EMIT_CHARACTER('-'); + } + ON('<') + { + SWITCH_TO_AND_EMIT_CHARACTER('<', ScriptDataDoubleEscapedLessThanSign); + } + ON('>') + { + SWITCH_TO_AND_EMIT_CHARACTER('>', ScriptData); + } + ON(0) + { + PARSE_ERROR(); + SWITCH_TO_AND_EMIT_CHARACTER(0xFFFD, ScriptDataDoubleEscaped); + } + ON_EOF + { + PARSE_ERROR(); + EMIT_EOF; + } + ANYTHING_ELSE + { + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped); + } + } + END_STATE + + BEGIN_STATE(ScriptDataDoubleEscapedLessThanSign) + { + ON('/') + { + m_temporary_buffer.clear(); + SWITCH_TO_AND_EMIT_CHARACTER('/', ScriptDataDoubleEscapeEnd); + } + ANYTHING_ELSE + { + RECONSUME_IN(ScriptDataDoubleEscaped); + } + } + END_STATE + + BEGIN_STATE(ScriptDataDoubleEscapeEnd) + { + auto temporary_buffer_equal_to_script = [this]() -> bool { + if (m_temporary_buffer.size() != 6) + return false; + + // FIXME: Is there a better way of doing this? + return m_temporary_buffer[0] == 's' && m_temporary_buffer[1] == 'c' && m_temporary_buffer[2] == 'r' && m_temporary_buffer[3] == 'i' && m_temporary_buffer[4] == 'p' && m_temporary_buffer[5] == 't'; + }; + ON_WHITESPACE + { + if (temporary_buffer_equal_to_script()) + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped); + else + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped); + } + ON('/') + { + if (temporary_buffer_equal_to_script()) + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped); + else + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped); + } + ON('>') + { + if (temporary_buffer_equal_to_script()) + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped); + else + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped); + } + ON_ASCII_UPPER_ALPHA + { + m_temporary_buffer.append(tolower(current_input_character.value())); + EMIT_CURRENT_CHARACTER; + } + ON_ASCII_LOWER_ALPHA + { + m_temporary_buffer.append(current_input_character.value()); + EMIT_CURRENT_CHARACTER; + } + ANYTHING_ELSE + { + RECONSUME_IN(ScriptDataDoubleEscaped); + } + } + END_STATE + + BEGIN_STATE(ScriptDataEscapedDash) + { + ON('-') + { + SWITCH_TO_AND_EMIT_CHARACTER('-', ScriptDataEscapedDashDash); + } + ON('<') + { + SWITCH_TO(ScriptDataEscapedLessThanSign); + } + ON(0) + { + PARSE_ERROR(); + SWITCH_TO_AND_EMIT_CHARACTER(0xFFFD, ScriptDataEscaped); + } + ON_EOF + { + PARSE_ERROR(); + EMIT_EOF; + } + ANYTHING_ELSE + { + SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped); + } + } + END_STATE + + BEGIN_STATE(ScriptDataEscaped) + { + ON('-') + { + SWITCH_TO_AND_EMIT_CHARACTER('-', ScriptDataEscapedDash); + } + ON('<') + { + SWITCH_TO(ScriptDataEscapedLessThanSign); + } + ON(0) + { + PARSE_ERROR(); + EMIT_CHARACTER(0xFFFD); + } + ON_EOF + { + PARSE_ERROR(); + EMIT_EOF; + } + ANYTHING_ELSE + { + EMIT_CURRENT_CHARACTER; + } + } + END_STATE + + BEGIN_STATE(ScriptDataEndTagOpen) + { + ON_ASCII_ALPHA + { + create_new_token(HTMLToken::Type::EndTag); + RECONSUME_IN(ScriptDataEndTagName); + } + ANYTHING_ELSE + { + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + RECONSUME_IN(ScriptData); + } + } + END_STATE + + BEGIN_STATE(ScriptDataEndTagName) + { + ON_WHITESPACE + { + if (current_end_tag_token_is_appropriate()) + SWITCH_TO(BeforeAttributeName); + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + RECONSUME_IN(ScriptData); + } + ON('/') + { + if (current_end_tag_token_is_appropriate()) + SWITCH_TO(SelfClosingStartTag); + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + RECONSUME_IN(ScriptData); + } + ON('>') + { + if (current_end_tag_token_is_appropriate()) + SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data); + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + RECONSUME_IN(ScriptData); + } + ON_ASCII_UPPER_ALPHA + { + m_current_token.m_tag.tag_name.append(tolower(current_input_character.value())); + m_temporary_buffer.append(current_input_character.value()); + continue; + } + ON_ASCII_LOWER_ALPHA + { + m_current_token.m_tag.tag_name.append(current_input_character.value()); + m_temporary_buffer.append(current_input_character.value()); + continue; + } + ANYTHING_ELSE + { + m_queued_tokens.enqueue(HTMLToken::make_character('<')); + m_queued_tokens.enqueue(HTMLToken::make_character('/')); + for (auto code_point : m_temporary_buffer) + m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); + RECONSUME_IN(ScriptData); + } + } + END_STATE + + BEGIN_STATE(CDATASection) + { + ON(']') + { + SWITCH_TO(CDATASectionBracket); + } + ON_EOF + { + PARSE_ERROR(); + EMIT_EOF; + } + ANYTHING_ELSE + { + EMIT_CURRENT_CHARACTER; + } + } + END_STATE + + BEGIN_STATE(CDATASectionBracket) + { + ON(']') + { + SWITCH_TO(CDATASectionEnd); + } + ANYTHING_ELSE + { + EMIT_CHARACTER_AND_RECONSUME_IN(']', CDATASection); + } + } + END_STATE + + BEGIN_STATE(CDATASectionEnd) + { + ON(']') + { + EMIT_CHARACTER(']'); + } + ON('>') + { + SWITCH_TO(Data); + } + ANYTHING_ELSE + { + m_queued_tokens.enqueue(HTMLToken::make_character(']')); + m_queued_tokens.enqueue(HTMLToken::make_character(']')); + RECONSUME_IN(CDATASection); + } + } + END_STATE + + default: + TODO(); + } + } +} + +bool HTMLTokenizer::consume_next_if_match(const StringView& string, CaseSensitivity case_sensitivity) +{ + for (size_t i = 0; i < string.length(); ++i) { + auto code_point = peek_code_point(i); + if (!code_point.has_value()) + return false; + // FIXME: This should be more Unicode-aware. + if (case_sensitivity == CaseSensitivity::CaseInsensitive) { + if (code_point.value() < 0x80) { + if (tolower(code_point.value()) != tolower(string[i])) + return false; + continue; + } + } + if (code_point.value() != (u32)string[i]) + return false; + } + for (size_t i = 0; i < string.length(); ++i) { + m_prev_utf8_iterator = m_utf8_iterator; + ++m_utf8_iterator; + } + return true; +} + +void HTMLTokenizer::create_new_token(HTMLToken::Type type) +{ + m_current_token = {}; + m_current_token.m_type = type; +} + +HTMLTokenizer::HTMLTokenizer(const StringView& input, const String& encoding) +{ + auto* decoder = TextCodec::decoder_for(encoding); + ASSERT(decoder); + m_decoded_input = decoder->to_utf8(input); + m_utf8_view = Utf8View(m_decoded_input); + m_utf8_iterator = m_utf8_view.begin(); +} + +void HTMLTokenizer::will_switch_to([[maybe_unused]] State new_state) +{ +#ifdef TOKENIZER_TRACE + dbg() << "[" << state_name(m_state) << "] Switch to " << state_name(new_state); +#endif +} + +void HTMLTokenizer::will_reconsume_in([[maybe_unused]] State new_state) +{ +#ifdef TOKENIZER_TRACE + dbg() << "[" << state_name(m_state) << "] Reconsume in " << state_name(new_state); +#endif +} + +void HTMLTokenizer::switch_to(Badge<HTMLDocumentParser>, State new_state) +{ +#ifdef TOKENIZER_TRACE + dbg() << "[" << state_name(m_state) << "] Parser switches tokenizer state to " << state_name(new_state); +#endif + m_state = new_state; +} + +void HTMLTokenizer::will_emit(HTMLToken& token) +{ + if (token.is_start_tag()) + m_last_emitted_start_tag = token; +} + +bool HTMLTokenizer::current_end_tag_token_is_appropriate() const +{ + ASSERT(m_current_token.is_end_tag()); + if (!m_last_emitted_start_tag.is_start_tag()) + return false; + return m_current_token.tag_name() == m_last_emitted_start_tag.tag_name(); +} + +bool HTMLTokenizer::consumed_as_part_of_an_attribute() const +{ + return m_return_state == State::AttributeValueUnquoted || m_return_state == State::AttributeValueSingleQuoted || m_return_state == State::AttributeValueDoubleQuoted; +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/Parser/HTMLTokenizer.h b/Userland/Libraries/LibWeb/HTML/Parser/HTMLTokenizer.h new file mode 100644 index 0000000000..787bc12b46 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Parser/HTMLTokenizer.h @@ -0,0 +1,190 @@ +/* + * 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 <AK/Queue.h> +#include <AK/StringView.h> +#include <AK/Types.h> +#include <AK/Utf8View.h> +#include <LibWeb/Forward.h> +#include <LibWeb/HTML/Parser/HTMLToken.h> + +namespace Web::HTML { + +#define ENUMERATE_TOKENIZER_STATES \ + __ENUMERATE_TOKENIZER_STATE(Data) \ + __ENUMERATE_TOKENIZER_STATE(RCDATA) \ + __ENUMERATE_TOKENIZER_STATE(RAWTEXT) \ + __ENUMERATE_TOKENIZER_STATE(ScriptData) \ + __ENUMERATE_TOKENIZER_STATE(PLAINTEXT) \ + __ENUMERATE_TOKENIZER_STATE(TagOpen) \ + __ENUMERATE_TOKENIZER_STATE(EndTagOpen) \ + __ENUMERATE_TOKENIZER_STATE(TagName) \ + __ENUMERATE_TOKENIZER_STATE(RCDATALessThanSign) \ + __ENUMERATE_TOKENIZER_STATE(RCDATAEndTagOpen) \ + __ENUMERATE_TOKENIZER_STATE(RCDATAEndTagName) \ + __ENUMERATE_TOKENIZER_STATE(RAWTEXTLessThanSign) \ + __ENUMERATE_TOKENIZER_STATE(RAWTEXTEndTagOpen) \ + __ENUMERATE_TOKENIZER_STATE(RAWTEXTEndTagName) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataLessThanSign) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataEndTagOpen) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataEndTagName) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataEscapeStart) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataEscapeStartDash) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataEscaped) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataEscapedDash) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataEscapedDashDash) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataEscapedLessThanSign) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataEscapedEndTagOpen) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataEscapedEndTagName) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataDoubleEscapeStart) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataDoubleEscaped) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataDoubleEscapedDash) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataDoubleEscapedDashDash) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataDoubleEscapedLessThanSign) \ + __ENUMERATE_TOKENIZER_STATE(ScriptDataDoubleEscapeEnd) \ + __ENUMERATE_TOKENIZER_STATE(BeforeAttributeName) \ + __ENUMERATE_TOKENIZER_STATE(AttributeName) \ + __ENUMERATE_TOKENIZER_STATE(AfterAttributeName) \ + __ENUMERATE_TOKENIZER_STATE(BeforeAttributeValue) \ + __ENUMERATE_TOKENIZER_STATE(AttributeValueDoubleQuoted) \ + __ENUMERATE_TOKENIZER_STATE(AttributeValueSingleQuoted) \ + __ENUMERATE_TOKENIZER_STATE(AttributeValueUnquoted) \ + __ENUMERATE_TOKENIZER_STATE(AfterAttributeValueQuoted) \ + __ENUMERATE_TOKENIZER_STATE(SelfClosingStartTag) \ + __ENUMERATE_TOKENIZER_STATE(BogusComment) \ + __ENUMERATE_TOKENIZER_STATE(MarkupDeclarationOpen) \ + __ENUMERATE_TOKENIZER_STATE(CommentStart) \ + __ENUMERATE_TOKENIZER_STATE(CommentStartDash) \ + __ENUMERATE_TOKENIZER_STATE(Comment) \ + __ENUMERATE_TOKENIZER_STATE(CommentLessThanSign) \ + __ENUMERATE_TOKENIZER_STATE(CommentLessThanSignBang) \ + __ENUMERATE_TOKENIZER_STATE(CommentLessThanSignBangDash) \ + __ENUMERATE_TOKENIZER_STATE(CommentLessThanSignBangDashDash) \ + __ENUMERATE_TOKENIZER_STATE(CommentEndDash) \ + __ENUMERATE_TOKENIZER_STATE(CommentEnd) \ + __ENUMERATE_TOKENIZER_STATE(CommentEndBang) \ + __ENUMERATE_TOKENIZER_STATE(DOCTYPE) \ + __ENUMERATE_TOKENIZER_STATE(BeforeDOCTYPEName) \ + __ENUMERATE_TOKENIZER_STATE(DOCTYPEName) \ + __ENUMERATE_TOKENIZER_STATE(AfterDOCTYPEName) \ + __ENUMERATE_TOKENIZER_STATE(AfterDOCTYPEPublicKeyword) \ + __ENUMERATE_TOKENIZER_STATE(BeforeDOCTYPEPublicIdentifier) \ + __ENUMERATE_TOKENIZER_STATE(DOCTYPEPublicIdentifierDoubleQuoted) \ + __ENUMERATE_TOKENIZER_STATE(DOCTYPEPublicIdentifierSingleQuoted) \ + __ENUMERATE_TOKENIZER_STATE(AfterDOCTYPEPublicIdentifier) \ + __ENUMERATE_TOKENIZER_STATE(BetweenDOCTYPEPublicAndSystemIdentifiers) \ + __ENUMERATE_TOKENIZER_STATE(AfterDOCTYPESystemKeyword) \ + __ENUMERATE_TOKENIZER_STATE(BeforeDOCTYPESystemIdentifier) \ + __ENUMERATE_TOKENIZER_STATE(DOCTYPESystemIdentifierDoubleQuoted) \ + __ENUMERATE_TOKENIZER_STATE(DOCTYPESystemIdentifierSingleQuoted) \ + __ENUMERATE_TOKENIZER_STATE(AfterDOCTYPESystemIdentifier) \ + __ENUMERATE_TOKENIZER_STATE(BogusDOCTYPE) \ + __ENUMERATE_TOKENIZER_STATE(CDATASection) \ + __ENUMERATE_TOKENIZER_STATE(CDATASectionBracket) \ + __ENUMERATE_TOKENIZER_STATE(CDATASectionEnd) \ + __ENUMERATE_TOKENIZER_STATE(CharacterReference) \ + __ENUMERATE_TOKENIZER_STATE(NamedCharacterReference) \ + __ENUMERATE_TOKENIZER_STATE(AmbiguousAmpersand) \ + __ENUMERATE_TOKENIZER_STATE(NumericCharacterReference) \ + __ENUMERATE_TOKENIZER_STATE(HexadecimalCharacterReferenceStart) \ + __ENUMERATE_TOKENIZER_STATE(DecimalCharacterReferenceStart) \ + __ENUMERATE_TOKENIZER_STATE(HexadecimalCharacterReference) \ + __ENUMERATE_TOKENIZER_STATE(DecimalCharacterReference) \ + __ENUMERATE_TOKENIZER_STATE(NumericCharacterReferenceEnd) + +class HTMLTokenizer { +public: + explicit HTMLTokenizer(const StringView& input, const String& encoding); + + enum class State { +#define __ENUMERATE_TOKENIZER_STATE(state) state, + ENUMERATE_TOKENIZER_STATES +#undef __ENUMERATE_TOKENIZER_STATE + }; + + Optional<HTMLToken> next_token(); + + void switch_to(Badge<HTMLDocumentParser>, State new_state); + + void set_blocked(bool b) { m_blocked = b; } + bool is_blocked() const { return m_blocked; } + + String source() const { return m_decoded_input; } + +private: + Optional<u32> next_code_point(); + Optional<u32> peek_code_point(size_t offset) const; + bool consume_next_if_match(const StringView&, CaseSensitivity = CaseSensitivity::CaseSensitive); + void create_new_token(HTMLToken::Type); + bool current_end_tag_token_is_appropriate() const; + + static const char* state_name(State state) + { + switch (state) { +#define __ENUMERATE_TOKENIZER_STATE(state) \ + case State::state: \ + return #state; + ENUMERATE_TOKENIZER_STATES +#undef __ENUMERATE_TOKENIZER_STATE + }; + ASSERT_NOT_REACHED(); + } + + void will_emit(HTMLToken&); + void will_switch_to(State); + void will_reconsume_in(State); + + bool consumed_as_part_of_an_attribute() const; + + State m_state { State::Data }; + State m_return_state { State::Data }; + + Vector<u32> m_temporary_buffer; + + String m_decoded_input; + + StringView m_input; + + Utf8View m_utf8_view; + AK::Utf8CodepointIterator m_utf8_iterator; + AK::Utf8CodepointIterator m_prev_utf8_iterator; + + HTMLToken m_current_token; + + HTMLToken m_last_emitted_start_tag; + + bool m_has_emitted_eof { false }; + + Queue<HTMLToken> m_queued_tokens; + + u32 m_character_reference_code { 0 }; + + bool m_blocked { false }; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/Parser/ListOfActiveFormattingElements.cpp b/Userland/Libraries/LibWeb/HTML/Parser/ListOfActiveFormattingElements.cpp new file mode 100644 index 0000000000..bad6140632 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Parser/ListOfActiveFormattingElements.cpp @@ -0,0 +1,84 @@ +/* + * 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 <LibWeb/DOM/Element.h> +#include <LibWeb/HTML/Parser/ListOfActiveFormattingElements.h> + +namespace Web::HTML { + +ListOfActiveFormattingElements::~ListOfActiveFormattingElements() +{ +} + +void ListOfActiveFormattingElements::add(DOM::Element& element) +{ + // FIXME: Implement the Noah's Ark clause https://html.spec.whatwg.org/multipage/parsing.html#push-onto-the-list-of-active-formatting-elements + m_entries.append({ element }); +} + +void ListOfActiveFormattingElements::add_marker() +{ + m_entries.append({ nullptr }); +} + +bool ListOfActiveFormattingElements::contains(const DOM::Element& element) const +{ + for (auto& entry : m_entries) { + if (entry.element == &element) + return true; + } + return false; +} + +DOM::Element* ListOfActiveFormattingElements::last_element_with_tag_name_before_marker(const FlyString& tag_name) +{ + for (ssize_t i = m_entries.size() - 1; i >= 0; --i) { + auto& entry = m_entries[i]; + if (entry.is_marker()) + return nullptr; + if (entry.element->local_name() == tag_name) + return entry.element; + } + return nullptr; +} + +void ListOfActiveFormattingElements::remove(DOM::Element& element) +{ + m_entries.remove_first_matching([&](auto& entry) { + return entry.element == &element; + }); +} + +void ListOfActiveFormattingElements::clear_up_to_the_last_marker() +{ + while (!m_entries.is_empty()) { + auto entry = m_entries.take_last(); + if (entry.is_marker()) + break; + } +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/Parser/ListOfActiveFormattingElements.h b/Userland/Libraries/LibWeb/HTML/Parser/ListOfActiveFormattingElements.h new file mode 100644 index 0000000000..65064be7e5 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Parser/ListOfActiveFormattingElements.h @@ -0,0 +1,65 @@ +/* + * 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 <AK/NonnullRefPtrVector.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/Forward.h> + +namespace Web::HTML { + +class ListOfActiveFormattingElements { +public: + ListOfActiveFormattingElements() { } + ~ListOfActiveFormattingElements(); + + struct Entry { + bool is_marker() const { return !element; } + + RefPtr<DOM::Element> element; + }; + + bool is_empty() const { return m_entries.is_empty(); } + bool contains(const DOM::Element&) const; + + void add(DOM::Element& element); + void add_marker(); + + void remove(DOM::Element&); + + const Vector<Entry>& entries() const { return m_entries; } + Vector<Entry>& entries() { return m_entries; } + + DOM::Element* last_element_with_tag_name_before_marker(const FlyString& tag_name); + + void clear_up_to_the_last_marker(); + +private: + Vector<Entry> m_entries; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.cpp b/Userland/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.cpp new file mode 100644 index 0000000000..2406711bff --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.cpp @@ -0,0 +1,159 @@ +/* + * 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 <LibWeb/DOM/Element.h> +#include <LibWeb/HTML/Parser/HTMLDocumentParser.h> +#include <LibWeb/HTML/Parser/StackOfOpenElements.h> + +namespace Web::HTML { + +static Vector<FlyString> s_base_list { "applet", "caption", "html", "table", "td", "th", "marquee", "object", "template" }; + +StackOfOpenElements::~StackOfOpenElements() +{ +} + +bool StackOfOpenElements::has_in_scope_impl(const FlyString& tag_name, const Vector<FlyString>& list) const +{ + for (ssize_t i = m_elements.size() - 1; i >= 0; --i) { + auto& node = m_elements.at(i); + if (node.local_name() == tag_name) + return true; + if (list.contains_slow(node.local_name())) + return false; + } + ASSERT_NOT_REACHED(); +} + +bool StackOfOpenElements::has_in_scope(const FlyString& tag_name) const +{ + return has_in_scope_impl(tag_name, s_base_list); +} + +bool StackOfOpenElements::has_in_scope_impl(const DOM::Element& target_node, const Vector<FlyString>& list) const +{ + for (ssize_t i = m_elements.size() - 1; i >= 0; --i) { + auto& node = m_elements.at(i); + if (&node == &target_node) + return true; + if (list.contains_slow(node.local_name())) + return false; + } + ASSERT_NOT_REACHED(); +} + +bool StackOfOpenElements::has_in_scope(const DOM::Element& target_node) const +{ + return has_in_scope_impl(target_node, s_base_list); +} + +bool StackOfOpenElements::has_in_button_scope(const FlyString& tag_name) const +{ + auto list = s_base_list; + list.append("button"); + return has_in_scope_impl(tag_name, list); +} + +bool StackOfOpenElements::has_in_table_scope(const FlyString& tag_name) const +{ + return has_in_scope_impl(tag_name, { "html", "table", "template" }); +} + +bool StackOfOpenElements::has_in_list_item_scope(const FlyString& tag_name) const +{ + auto list = s_base_list; + list.append("ol"); + list.append("ul"); + return has_in_scope_impl(tag_name, list); +} + +bool StackOfOpenElements::has_in_select_scope(const FlyString& tag_name) const +{ + return has_in_scope_impl(tag_name, { "option", "optgroup" }); +} + +bool StackOfOpenElements::contains(const DOM::Element& element) const +{ + for (auto& element_on_stack : m_elements) { + if (&element == &element_on_stack) + return true; + } + return false; +} + +bool StackOfOpenElements::contains(const FlyString& tag_name) const +{ + for (auto& element_on_stack : m_elements) { + if (element_on_stack.local_name() == tag_name) + return true; + } + return false; +} + +void StackOfOpenElements::pop_until_an_element_with_tag_name_has_been_popped(const FlyString& tag_name) +{ + while (m_elements.last().local_name() != tag_name) + pop(); + pop(); +} + +DOM::Element* StackOfOpenElements::topmost_special_node_below(const DOM::Element& formatting_element) +{ + DOM::Element* found_element = nullptr; + for (ssize_t i = m_elements.size() - 1; i >= 0; --i) { + auto& element = m_elements[i]; + if (&element == &formatting_element) + break; + if (HTMLDocumentParser::is_special_tag(element.local_name(), element.namespace_())) + found_element = &element; + } + return found_element; +} + +StackOfOpenElements::LastElementResult StackOfOpenElements::last_element_with_tag_name(const FlyString& tag_name) +{ + for (ssize_t i = m_elements.size() - 1; i >= 0; --i) { + auto& element = m_elements[i]; + if (element.local_name() == tag_name) + return { &element, i }; + } + return { nullptr, -1 }; +} + +DOM::Element* StackOfOpenElements::element_before(const DOM::Element& target) +{ + bool found_target = false; + for (ssize_t i = m_elements.size() - 1; i >= 0; --i) { + auto& element = m_elements[i]; + if (&element == &target) { + found_target = true; + } else if (found_target) + return &element; + } + return nullptr; +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.h b/Userland/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.h new file mode 100644 index 0000000000..2a62bbf53e --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.h @@ -0,0 +1,82 @@ +/* + * 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 <AK/NonnullRefPtrVector.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/Forward.h> + +namespace Web::HTML { + +class StackOfOpenElements { +public: + StackOfOpenElements() { } + ~StackOfOpenElements(); + + DOM::Element& first() { return m_elements.first(); } + DOM::Element& last() { return m_elements.last(); } + + bool is_empty() const { return m_elements.is_empty(); } + void push(NonnullRefPtr<DOM::Element> element) { m_elements.append(move(element)); } + NonnullRefPtr<DOM::Element> pop() { return m_elements.take_last(); } + + const DOM::Element& current_node() const { return m_elements.last(); } + DOM::Element& current_node() { return m_elements.last(); } + + bool has_in_scope(const FlyString& tag_name) const; + bool has_in_button_scope(const FlyString& tag_name) const; + bool has_in_table_scope(const FlyString& tag_name) const; + bool has_in_list_item_scope(const FlyString& tag_name) const; + bool has_in_select_scope(const FlyString& tag_name) const; + + bool has_in_scope(const DOM::Element&) const; + + bool contains(const DOM::Element&) const; + bool contains(const FlyString& tag_name) const; + + const NonnullRefPtrVector<DOM::Element>& elements() const { return m_elements; } + NonnullRefPtrVector<DOM::Element>& elements() { return m_elements; } + + void pop_until_an_element_with_tag_name_has_been_popped(const FlyString&); + + DOM::Element* topmost_special_node_below(const DOM::Element&); + + struct LastElementResult { + DOM::Element* element; + ssize_t index; + }; + LastElementResult last_element_with_tag_name(const FlyString&); + DOM::Element* element_before(const DOM::Element&); + +private: + bool has_in_scope_impl(const FlyString& tag_name, const Vector<FlyString>&) const; + bool has_in_scope_impl(const DOM::Element& target_node, const Vector<FlyString>&) const; + + NonnullRefPtrVector<DOM::Element> m_elements; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/SubmitEvent.h b/Userland/Libraries/LibWeb/HTML/SubmitEvent.h new file mode 100644 index 0000000000..06c4b6fb0b --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/SubmitEvent.h @@ -0,0 +1,54 @@ +/* + * 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 <LibWeb/DOM/Event.h> + +namespace Web::HTML { + +class SubmitEvent final : public DOM::Event { +public: + using WrapperType = Bindings::SubmitEventWrapper; + + static NonnullRefPtr<SubmitEvent> create(const FlyString& event_name, RefPtr<HTMLElement> submitter) + { + return adopt(*new SubmitEvent(event_name, submitter)); + } + + const RefPtr<HTMLElement> submitter() const { return m_submitter; } + +private: + SubmitEvent(const FlyString& event_name, RefPtr<HTMLElement> submitter) + : DOM::Event(event_name) + , m_submitter(submitter) + { + } + + RefPtr<HTMLElement> m_submitter; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/SubmitEvent.idl b/Userland/Libraries/LibWeb/HTML/SubmitEvent.idl new file mode 100644 index 0000000000..c816ae663f --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/SubmitEvent.idl @@ -0,0 +1,5 @@ +interface SubmitEvent : Event { + + readonly attribute HTMLElement? submitter; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/TagNames.cpp b/Userland/Libraries/LibWeb/HTML/TagNames.cpp new file mode 100644 index 0000000000..1ab76b7376 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/TagNames.cpp @@ -0,0 +1,51 @@ +/* + * 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 <LibWeb/HTML/TagNames.h> + +namespace Web::HTML::TagNames { + +#define __ENUMERATE_HTML_TAG(name) FlyString name; +ENUMERATE_HTML_TAGS +#undef __ENUMERATE_HTML_TAG + +[[gnu::constructor]] static void initialize() +{ + static bool s_initialized = false; + if (s_initialized) + return; + +#define __ENUMERATE_HTML_TAG(name) \ + name = #name; + ENUMERATE_HTML_TAGS +#undef __ENUMERATE_HTML_TAG + + template_ = "template"; + + s_initialized = true; +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/TagNames.h b/Userland/Libraries/LibWeb/HTML/TagNames.h new file mode 100644 index 0000000000..9d7f924e2a --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/TagNames.h @@ -0,0 +1,178 @@ +/* + * 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 <AK/FlyString.h> + +namespace Web::HTML::TagNames { + +#define ENUMERATE_HTML_TAGS \ + __ENUMERATE_HTML_TAG(a) \ + __ENUMERATE_HTML_TAG(abbr) \ + __ENUMERATE_HTML_TAG(acronym) \ + __ENUMERATE_HTML_TAG(address) \ + __ENUMERATE_HTML_TAG(applet) \ + __ENUMERATE_HTML_TAG(area) \ + __ENUMERATE_HTML_TAG(article) \ + __ENUMERATE_HTML_TAG(aside) \ + __ENUMERATE_HTML_TAG(audio) \ + __ENUMERATE_HTML_TAG(b) \ + __ENUMERATE_HTML_TAG(base) \ + __ENUMERATE_HTML_TAG(basefont) \ + __ENUMERATE_HTML_TAG(bdi) \ + __ENUMERATE_HTML_TAG(bdo) \ + __ENUMERATE_HTML_TAG(bgsound) \ + __ENUMERATE_HTML_TAG(big) \ + __ENUMERATE_HTML_TAG(blink) \ + __ENUMERATE_HTML_TAG(blockquote) \ + __ENUMERATE_HTML_TAG(body) \ + __ENUMERATE_HTML_TAG(br) \ + __ENUMERATE_HTML_TAG(button) \ + __ENUMERATE_HTML_TAG(canvas) \ + __ENUMERATE_HTML_TAG(caption) \ + __ENUMERATE_HTML_TAG(center) \ + __ENUMERATE_HTML_TAG(cite) \ + __ENUMERATE_HTML_TAG(code) \ + __ENUMERATE_HTML_TAG(col) \ + __ENUMERATE_HTML_TAG(colgroup) \ + __ENUMERATE_HTML_TAG(data) \ + __ENUMERATE_HTML_TAG(datalist) \ + __ENUMERATE_HTML_TAG(dd) \ + __ENUMERATE_HTML_TAG(del) \ + __ENUMERATE_HTML_TAG(details) \ + __ENUMERATE_HTML_TAG(dfn) \ + __ENUMERATE_HTML_TAG(dialog) \ + __ENUMERATE_HTML_TAG(dir) \ + __ENUMERATE_HTML_TAG(div) \ + __ENUMERATE_HTML_TAG(dl) \ + __ENUMERATE_HTML_TAG(dt) \ + __ENUMERATE_HTML_TAG(em) \ + __ENUMERATE_HTML_TAG(embed) \ + __ENUMERATE_HTML_TAG(fieldset) \ + __ENUMERATE_HTML_TAG(figcaption) \ + __ENUMERATE_HTML_TAG(figure) \ + __ENUMERATE_HTML_TAG(font) \ + __ENUMERATE_HTML_TAG(footer) \ + __ENUMERATE_HTML_TAG(form) \ + __ENUMERATE_HTML_TAG(frame) \ + __ENUMERATE_HTML_TAG(frameset) \ + __ENUMERATE_HTML_TAG(h1) \ + __ENUMERATE_HTML_TAG(h2) \ + __ENUMERATE_HTML_TAG(h3) \ + __ENUMERATE_HTML_TAG(h4) \ + __ENUMERATE_HTML_TAG(h5) \ + __ENUMERATE_HTML_TAG(h6) \ + __ENUMERATE_HTML_TAG(head) \ + __ENUMERATE_HTML_TAG(header) \ + __ENUMERATE_HTML_TAG(hgroup) \ + __ENUMERATE_HTML_TAG(hr) \ + __ENUMERATE_HTML_TAG(html) \ + __ENUMERATE_HTML_TAG(i) \ + __ENUMERATE_HTML_TAG(iframe) \ + __ENUMERATE_HTML_TAG(image) \ + __ENUMERATE_HTML_TAG(img) \ + __ENUMERATE_HTML_TAG(input) \ + __ENUMERATE_HTML_TAG(ins) \ + __ENUMERATE_HTML_TAG(kbd) \ + __ENUMERATE_HTML_TAG(keygen) \ + __ENUMERATE_HTML_TAG(label) \ + __ENUMERATE_HTML_TAG(legend) \ + __ENUMERATE_HTML_TAG(li) \ + __ENUMERATE_HTML_TAG(link) \ + __ENUMERATE_HTML_TAG(listing) \ + __ENUMERATE_HTML_TAG(main) \ + __ENUMERATE_HTML_TAG(map) \ + __ENUMERATE_HTML_TAG(mark) \ + __ENUMERATE_HTML_TAG(marquee) \ + __ENUMERATE_HTML_TAG(math) \ + __ENUMERATE_HTML_TAG(menu) \ + __ENUMERATE_HTML_TAG(meta) \ + __ENUMERATE_HTML_TAG(meter) \ + __ENUMERATE_HTML_TAG(nav) \ + __ENUMERATE_HTML_TAG(nobr) \ + __ENUMERATE_HTML_TAG(noembed) \ + __ENUMERATE_HTML_TAG(noframes) \ + __ENUMERATE_HTML_TAG(noscript) \ + __ENUMERATE_HTML_TAG(object) \ + __ENUMERATE_HTML_TAG(ol) \ + __ENUMERATE_HTML_TAG(optgroup) \ + __ENUMERATE_HTML_TAG(option) \ + __ENUMERATE_HTML_TAG(output) \ + __ENUMERATE_HTML_TAG(p) \ + __ENUMERATE_HTML_TAG(param) \ + __ENUMERATE_HTML_TAG(picture) \ + __ENUMERATE_HTML_TAG(path) \ + __ENUMERATE_HTML_TAG(plaintext) \ + __ENUMERATE_HTML_TAG(pre) \ + __ENUMERATE_HTML_TAG(progress) \ + __ENUMERATE_HTML_TAG(q) \ + __ENUMERATE_HTML_TAG(ruby) \ + __ENUMERATE_HTML_TAG(rb) \ + __ENUMERATE_HTML_TAG(rp) \ + __ENUMERATE_HTML_TAG(rt) \ + __ENUMERATE_HTML_TAG(rtc) \ + __ENUMERATE_HTML_TAG(s) \ + __ENUMERATE_HTML_TAG(samp) \ + __ENUMERATE_HTML_TAG(script) \ + __ENUMERATE_HTML_TAG(section) \ + __ENUMERATE_HTML_TAG(select) \ + __ENUMERATE_HTML_TAG(slot) \ + __ENUMERATE_HTML_TAG(small) \ + __ENUMERATE_HTML_TAG(source) \ + __ENUMERATE_HTML_TAG(span) \ + __ENUMERATE_HTML_TAG(strike) \ + __ENUMERATE_HTML_TAG(strong) \ + __ENUMERATE_HTML_TAG(style) \ + __ENUMERATE_HTML_TAG(sub) \ + __ENUMERATE_HTML_TAG(sup) \ + __ENUMERATE_HTML_TAG(summary) \ + __ENUMERATE_HTML_TAG(svg) \ + __ENUMERATE_HTML_TAG(table) \ + __ENUMERATE_HTML_TAG(tbody) \ + __ENUMERATE_HTML_TAG(td) \ + __ENUMERATE_HTML_TAG(template_) \ + __ENUMERATE_HTML_TAG(textarea) \ + __ENUMERATE_HTML_TAG(tfoot) \ + __ENUMERATE_HTML_TAG(th) \ + __ENUMERATE_HTML_TAG(thead) \ + __ENUMERATE_HTML_TAG(time) \ + __ENUMERATE_HTML_TAG(title) \ + __ENUMERATE_HTML_TAG(tr) \ + __ENUMERATE_HTML_TAG(track) \ + __ENUMERATE_HTML_TAG(tt) \ + __ENUMERATE_HTML_TAG(u) \ + __ENUMERATE_HTML_TAG(ul) \ + __ENUMERATE_HTML_TAG(var) \ + __ENUMERATE_HTML_TAG(video) \ + __ENUMERATE_HTML_TAG(wbr) \ + __ENUMERATE_HTML_TAG(xmp) + +#define __ENUMERATE_HTML_TAG(name) extern FlyString name; +ENUMERATE_HTML_TAGS +#undef __ENUMERATE_HTML_TAG + +} diff --git a/Userland/Libraries/LibWeb/HighResolutionTime/Performance.cpp b/Userland/Libraries/LibWeb/HighResolutionTime/Performance.cpp new file mode 100644 index 0000000000..cdefa63eff --- /dev/null +++ b/Userland/Libraries/LibWeb/HighResolutionTime/Performance.cpp @@ -0,0 +1,73 @@ +/* + * 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 <LibWeb/Bindings/PerformanceWrapper.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/DOM/EventDispatcher.h> +#include <LibWeb/DOM/Window.h> +#include <LibWeb/HighResolutionTime/Performance.h> + +namespace Web::HighResolutionTime { + +Performance::Performance(DOM::Window& window) + : DOM::EventTarget(static_cast<Bindings::ScriptExecutionContext&>(window.document())) + , m_window(window) +{ + m_timer.start(); +} + +Performance::~Performance() +{ +} + +double Performance::time_origin() const +{ + auto origin = m_timer.origin_time(); + return (origin.tv_sec * 1000.0) + (origin.tv_usec / 1000.0); +} + +void Performance::ref_event_target() +{ + m_window.ref(); +} + +void Performance::unref_event_target() +{ + m_window.unref(); +} + +bool Performance::dispatch_event(NonnullRefPtr<DOM::Event> event) +{ + return DOM::EventDispatcher::dispatch(*this, event); +} + +Bindings::EventTargetWrapper* Performance::create_wrapper(JS::GlobalObject& global_object) +{ + return Bindings::wrap(global_object, *this); +} + +} diff --git a/Userland/Libraries/LibWeb/HighResolutionTime/Performance.h b/Userland/Libraries/LibWeb/HighResolutionTime/Performance.h new file mode 100644 index 0000000000..d5a9effbf5 --- /dev/null +++ b/Userland/Libraries/LibWeb/HighResolutionTime/Performance.h @@ -0,0 +1,60 @@ +/* + * 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 <AK/StdLibExtras.h> +#include <LibCore/ElapsedTimer.h> +#include <LibWeb/Bindings/Wrappable.h> +#include <LibWeb/DOM/EventTarget.h> + +namespace Web::HighResolutionTime { + +class Performance final + : public DOM::EventTarget + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::PerformanceWrapper; + using AllowOwnPtr = AK::TrueType; + + explicit Performance(DOM::Window&); + ~Performance(); + + double now() const { return m_timer.elapsed(); } + double time_origin() const; + + virtual void ref_event_target() override; + virtual void unref_event_target() override; + + virtual bool dispatch_event(NonnullRefPtr<DOM::Event>) override; + virtual Bindings::EventTargetWrapper* create_wrapper(JS::GlobalObject&) override; + +private: + DOM::Window& m_window; + Core::ElapsedTimer m_timer; +}; + +} diff --git a/Userland/Libraries/LibWeb/HighResolutionTime/Performance.idl b/Userland/Libraries/LibWeb/HighResolutionTime/Performance.idl new file mode 100644 index 0000000000..889760a08a --- /dev/null +++ b/Userland/Libraries/LibWeb/HighResolutionTime/Performance.idl @@ -0,0 +1,4 @@ +interface Performance : EventTarget { + double now(); + readonly attribute double timeOrigin; +}; diff --git a/Userland/Libraries/LibWeb/InProcessWebView.cpp b/Userland/Libraries/LibWeb/InProcessWebView.cpp new file mode 100644 index 0000000000..84b8dfec07 --- /dev/null +++ b/Userland/Libraries/LibWeb/InProcessWebView.cpp @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2018-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/LexicalPath.h> +#include <AK/URL.h> +#include <LibCore/File.h> +#include <LibCore/MimeData.h> +#include <LibGUI/Action.h> +#include <LibGUI/Application.h> +#include <LibGUI/Clipboard.h> +#include <LibGUI/MessageBox.h> +#include <LibGUI/Painter.h> +#include <LibGUI/ScrollBar.h> +#include <LibGUI/Window.h> +#include <LibGfx/ImageDecoder.h> +#include <LibGfx/ShareableBitmap.h> +#include <LibJS/Runtime/Value.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/DOM/ElementFactory.h> +#include <LibWeb/DOM/Text.h> +#include <LibWeb/Dump.h> +#include <LibWeb/HTML/HTMLAnchorElement.h> +#include <LibWeb/HTML/HTMLImageElement.h> +#include <LibWeb/HTML/Parser/HTMLDocumentParser.h> +#include <LibWeb/InProcessWebView.h> +#include <LibWeb/Layout/BreakNode.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> +#include <LibWeb/Layout/Node.h> +#include <LibWeb/Layout/TextNode.h> +#include <LibWeb/Loader/ResourceLoader.h> +#include <LibWeb/Page/EventHandler.h> +#include <LibWeb/Page/Frame.h> +#include <LibWeb/Painting/PaintContext.h> +#include <LibWeb/UIEvents/MouseEvent.h> +#include <stdio.h> + +//#define SELECTION_DEBUG + +REGISTER_WIDGET(Web, InProcessWebView) + +namespace Web { + +InProcessWebView::InProcessWebView() + : m_page(make<Page>(*this)) +{ + set_should_hide_unnecessary_scrollbars(true); + set_background_role(ColorRole::Base); + set_focus_policy(GUI::FocusPolicy::StrongFocus); + + m_copy_action = GUI::CommonActions::make_copy_action([this](auto&) { + GUI::Clipboard::the().set_plain_text(selected_text()); + }); + + m_select_all_action = GUI::CommonActions::make_select_all_action([this](auto&) { + select_all(); + }); +} + +InProcessWebView::~InProcessWebView() +{ +} + +void InProcessWebView::select_all() +{ + auto* layout_root = this->layout_root(); + if (!layout_root) + return; + + const Layout::Node* first_layout_node = layout_root; + + for (;;) { + auto* next = first_layout_node->next_in_pre_order(); + if (!next) + break; + first_layout_node = next; + if (is<Layout::TextNode>(*first_layout_node)) + break; + } + + const Layout::Node* last_layout_node = first_layout_node; + + for (const Layout::Node* layout_node = first_layout_node; layout_node; layout_node = layout_node->next_in_pre_order()) { + if (is<Layout::TextNode>(*layout_node)) + last_layout_node = layout_node; + } + + ASSERT(first_layout_node); + ASSERT(last_layout_node); + + int last_layout_node_index_in_node = 0; + if (is<Layout::TextNode>(*last_layout_node)) + last_layout_node_index_in_node = downcast<Layout::TextNode>(*last_layout_node).text_for_rendering().length() - 1; + + layout_root->set_selection({ { first_layout_node, 0 }, { last_layout_node, last_layout_node_index_in_node } }); + update(); +} + +String InProcessWebView::selected_text() const +{ + return page().focused_frame().selected_text(); +} + +void InProcessWebView::page_did_layout() +{ + ASSERT(layout_root()); + set_content_size(layout_root()->size().to_type<int>()); +} + +void InProcessWebView::page_did_change_title(const String& title) +{ + if (on_title_change) + on_title_change(title); +} + +void InProcessWebView::page_did_set_document_in_main_frame(DOM::Document* document) +{ + if (on_set_document) + on_set_document(document); + layout_and_sync_size(); + scroll_to_top(); + update(); +} + +void InProcessWebView::page_did_start_loading(const URL& url) +{ + if (on_load_start) + on_load_start(url); +} + +void InProcessWebView::page_did_finish_loading(const URL& url) +{ + if (on_load_finish) + on_load_finish(url); +} + +void InProcessWebView::page_did_change_selection() +{ + update(); +} + +void InProcessWebView::page_did_request_cursor_change(Gfx::StandardCursor cursor) +{ + set_override_cursor(cursor); +} + +void InProcessWebView::page_did_request_context_menu(const Gfx::IntPoint& content_position) +{ + if (on_context_menu_request) + on_context_menu_request(screen_relative_rect().location().translated(to_widget_position(content_position))); +} + +void InProcessWebView::page_did_request_link_context_menu(const Gfx::IntPoint& content_position, const URL& url, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers) +{ + if (on_link_context_menu_request) + on_link_context_menu_request(url, screen_relative_rect().location().translated(to_widget_position(content_position))); +} + +void InProcessWebView::page_did_request_image_context_menu(const Gfx::IntPoint& content_position, const URL& url, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers, const Gfx::Bitmap* bitmap) +{ + if (!on_image_context_menu_request) + return; + Gfx::ShareableBitmap shareable_bitmap; + if (bitmap) + shareable_bitmap = Gfx::ShareableBitmap(*bitmap); + on_image_context_menu_request(url, screen_relative_rect().location().translated(to_widget_position(content_position)), shareable_bitmap); +} + +void InProcessWebView::page_did_click_link(const URL& url, const String& target, unsigned modifiers) +{ + if (on_link_click) + on_link_click(url, target, modifiers); +} + +void InProcessWebView::page_did_middle_click_link(const URL& url, const String& target, unsigned modifiers) +{ + if (on_link_middle_click) + on_link_middle_click(url, target, modifiers); +} + +void InProcessWebView::page_did_enter_tooltip_area([[maybe_unused]] const Gfx::IntPoint& content_position, const String& title) +{ + GUI::Application::the()->show_tooltip(title, nullptr); +} + +void InProcessWebView::page_did_leave_tooltip_area() +{ + GUI::Application::the()->hide_tooltip(); +} + +void InProcessWebView::page_did_hover_link(const URL& url) +{ + if (on_link_hover) + on_link_hover(url); +} + +void InProcessWebView::page_did_unhover_link() +{ + if (on_link_hover) + on_link_hover({}); +} + +void InProcessWebView::page_did_invalidate(const Gfx::IntRect&) +{ + update(); +} + +void InProcessWebView::page_did_change_favicon(const Gfx::Bitmap& bitmap) +{ + if (on_favicon_change) + on_favicon_change(bitmap); +} + +void InProcessWebView::layout_and_sync_size() +{ + if (!document()) + return; + + bool had_vertical_scrollbar = vertical_scrollbar().is_visible(); + bool had_horizontal_scrollbar = horizontal_scrollbar().is_visible(); + + page().main_frame().set_size(available_size()); + set_content_size(layout_root()->size().to_type<int>()); + + // NOTE: If layout caused us to gain or lose scrollbars, we have to lay out again + // since the scrollbars now take up some of the available space. + if (had_vertical_scrollbar != vertical_scrollbar().is_visible() || had_horizontal_scrollbar != horizontal_scrollbar().is_visible()) { + page().main_frame().set_size(available_size()); + set_content_size(layout_root()->size().to_type<int>()); + } + + page().main_frame().set_viewport_scroll_offset({ horizontal_scrollbar().value(), vertical_scrollbar().value() }); +} + +void InProcessWebView::resize_event(GUI::ResizeEvent& event) +{ + GUI::ScrollableWidget::resize_event(event); + layout_and_sync_size(); +} + +void InProcessWebView::paint_event(GUI::PaintEvent& event) +{ + GUI::Frame::paint_event(event); + + GUI::Painter painter(*this); + painter.add_clip_rect(widget_inner_rect()); + painter.add_clip_rect(event.rect()); + + if (!layout_root()) { + painter.fill_rect(event.rect(), palette().color(background_role())); + return; + } + + painter.fill_rect(event.rect(), document()->background_color(palette())); + + if (auto background_bitmap = document()->background_image()) { + painter.draw_tiled_bitmap(event.rect(), *background_bitmap); + } + + painter.translate(frame_thickness(), frame_thickness()); + painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value()); + + PaintContext context(painter, palette(), { horizontal_scrollbar().value(), vertical_scrollbar().value() }); + context.set_should_show_line_box_borders(m_should_show_line_box_borders); + context.set_viewport_rect(viewport_rect_in_content_coordinates()); + context.set_has_focus(is_focused()); + layout_root()->paint_all_phases(context); +} + +void InProcessWebView::mousemove_event(GUI::MouseEvent& event) +{ + page().handle_mousemove(to_content_position(event.position()), event.buttons(), event.modifiers()); + GUI::ScrollableWidget::mousemove_event(event); +} + +void InProcessWebView::mousedown_event(GUI::MouseEvent& event) +{ + page().handle_mousedown(to_content_position(event.position()), event.button(), event.modifiers()); + GUI::ScrollableWidget::mousedown_event(event); +} + +void InProcessWebView::mouseup_event(GUI::MouseEvent& event) +{ + page().handle_mouseup(to_content_position(event.position()), event.button(), event.modifiers()); + GUI::ScrollableWidget::mouseup_event(event); +} + +void InProcessWebView::keydown_event(GUI::KeyEvent& event) +{ + bool page_accepted_event = page().handle_keydown(event.key(), event.modifiers(), event.code_point()); + + if (event.modifiers() == 0) { + switch (event.key()) { + case Key_Home: + vertical_scrollbar().set_value(0); + break; + case Key_End: + vertical_scrollbar().set_value(vertical_scrollbar().max()); + break; + case Key_Down: + vertical_scrollbar().set_value(vertical_scrollbar().value() + vertical_scrollbar().step()); + break; + case Key_Up: + vertical_scrollbar().set_value(vertical_scrollbar().value() - vertical_scrollbar().step()); + break; + case Key_Left: + horizontal_scrollbar().set_value(horizontal_scrollbar().value() + horizontal_scrollbar().step()); + break; + case Key_Right: + horizontal_scrollbar().set_value(horizontal_scrollbar().value() - horizontal_scrollbar().step()); + break; + case Key_PageDown: + vertical_scrollbar().set_value(vertical_scrollbar().value() + frame_inner_rect().height()); + break; + case Key_PageUp: + vertical_scrollbar().set_value(vertical_scrollbar().value() - frame_inner_rect().height()); + break; + default: + if (!page_accepted_event) { + ScrollableWidget::keydown_event(event); + return; + } + break; + } + } + + event.accept(); +} + +URL InProcessWebView::url() const +{ + if (!page().main_frame().document()) + return {}; + return page().main_frame().document()->url(); +} + +void InProcessWebView::reload() +{ + load(url()); +} + +void InProcessWebView::load_html(const StringView& html, const URL& url) +{ + page().main_frame().loader().load_html(html, url); +} + +bool InProcessWebView::load(const URL& url) +{ + set_override_cursor(Gfx::StandardCursor::None); + return page().main_frame().loader().load(url, FrameLoader::Type::Navigation); +} + +const Layout::InitialContainingBlockBox* InProcessWebView::layout_root() const +{ + return document() ? document()->layout_node() : nullptr; +} + +Layout::InitialContainingBlockBox* InProcessWebView::layout_root() +{ + if (!document()) + return nullptr; + return const_cast<Layout::InitialContainingBlockBox*>(document()->layout_node()); +} + +void InProcessWebView::page_did_request_scroll_into_view(const Gfx::IntRect& rect) +{ + scroll_into_view(rect, true, true); + set_override_cursor(Gfx::StandardCursor::None); +} + +void InProcessWebView::load_empty_document() +{ + page().main_frame().set_document(nullptr); +} + +DOM::Document* InProcessWebView::document() +{ + return page().main_frame().document(); +} + +const DOM::Document* InProcessWebView::document() const +{ + return page().main_frame().document(); +} + +void InProcessWebView::set_document(DOM::Document* document) +{ + page().main_frame().set_document(document); +} + +void InProcessWebView::did_scroll() +{ + page().main_frame().set_viewport_scroll_offset({ horizontal_scrollbar().value(), vertical_scrollbar().value() }); + page().main_frame().did_scroll({}); +} + +void InProcessWebView::drop_event(GUI::DropEvent& event) +{ + if (event.mime_data().has_urls()) { + if (on_url_drop) { + on_url_drop(event.mime_data().urls().first()); + return; + } + } + ScrollableWidget::drop_event(event); +} + +void InProcessWebView::page_did_request_alert(const String& message) +{ + GUI::MessageBox::show(window(), message, "Alert", GUI::MessageBox::Type::Information); +} + +} diff --git a/Userland/Libraries/LibWeb/InProcessWebView.h b/Userland/Libraries/LibWeb/InProcessWebView.h new file mode 100644 index 0000000000..084c3a01c4 --- /dev/null +++ b/Userland/Libraries/LibWeb/InProcessWebView.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018-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 <AK/URL.h> +#include <LibGUI/ScrollableWidget.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/Page/Page.h> +#include <LibWeb/WebViewHooks.h> + +namespace Web { + +class InProcessWebView final + : public GUI::ScrollableWidget + , public WebViewHooks + , public PageClient { + C_OBJECT(InProcessWebView); + +public: + virtual ~InProcessWebView() override; + + void load_html(const StringView&, const URL&); + void load_empty_document(); + + DOM::Document* document(); + const DOM::Document* document() const; + + void set_document(DOM::Document*); + + const Layout::InitialContainingBlockBox* layout_root() const; + Layout::InitialContainingBlockBox* layout_root(); + + void reload(); + bool load(const URL&); + + URL url() const; + + void set_should_show_line_box_borders(bool value) { m_should_show_line_box_borders = value; } + + GUI::Action& select_all_action() { return *m_select_all_action; } + GUI::Action& copy_action() { return *m_copy_action; } + + String selected_text() const; + void select_all(); + +private: + InProcessWebView(); + + Page& page() { return *m_page; } + const Page& page() const { return *m_page; } + + virtual void resize_event(GUI::ResizeEvent&) override; + virtual void paint_event(GUI::PaintEvent&) override; + virtual void mousemove_event(GUI::MouseEvent&) override; + virtual void mousedown_event(GUI::MouseEvent&) override; + virtual void mouseup_event(GUI::MouseEvent&) override; + virtual void keydown_event(GUI::KeyEvent&) override; + virtual void drop_event(GUI::DropEvent&) override; + + virtual void did_scroll() override; + + // ^Web::PageClient + virtual Gfx::Palette palette() const override { return GUI::ScrollableWidget::palette(); } + virtual void page_did_change_title(const String&) override; + virtual void page_did_set_document_in_main_frame(DOM::Document*) override; + virtual void page_did_start_loading(const URL&) override; + virtual void page_did_finish_loading(const URL&) override; + virtual void page_did_change_selection() override; + virtual void page_did_request_cursor_change(Gfx::StandardCursor) override; + virtual void page_did_request_context_menu(const Gfx::IntPoint&) override; + virtual void page_did_request_link_context_menu(const Gfx::IntPoint&, const URL&, const String& target, unsigned modifiers) override; + virtual void page_did_request_image_context_menu(const Gfx::IntPoint&, const URL&, const String& target, unsigned modifiers, const Gfx::Bitmap*) override; + virtual void page_did_click_link(const URL&, const String& target, unsigned modifiers) override; + virtual void page_did_middle_click_link(const URL&, const String& target, unsigned modifiers) override; + virtual void page_did_enter_tooltip_area(const Gfx::IntPoint&, const String&) override; + virtual void page_did_leave_tooltip_area() override; + virtual void page_did_hover_link(const URL&) override; + virtual void page_did_unhover_link() override; + virtual void page_did_invalidate(const Gfx::IntRect&) override; + virtual void page_did_change_favicon(const Gfx::Bitmap&) override; + virtual void page_did_layout() override; + virtual void page_did_request_scroll_into_view(const Gfx::IntRect&) override; + virtual void page_did_request_alert(const String&) override; + + void layout_and_sync_size(); + + bool m_should_show_line_box_borders { false }; + + NonnullOwnPtr<Page> m_page; + + RefPtr<GUI::Action> m_copy_action; + RefPtr<GUI::Action> m_select_all_action; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/BlockBox.cpp b/Userland/Libraries/LibWeb/Layout/BlockBox.cpp new file mode 100644 index 0000000000..aab1430afc --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/BlockBox.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2018-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 <LibGUI/Painter.h> +#include <LibWeb/CSS/StyleResolver.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/Dump.h> +#include <LibWeb/Layout/BlockBox.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> +#include <LibWeb/Layout/InlineFormattingContext.h> +#include <LibWeb/Layout/InlineNode.h> +#include <LibWeb/Layout/ReplacedBox.h> +#include <LibWeb/Layout/TextNode.h> +#include <LibWeb/Layout/WidgetBox.h> +#include <math.h> + +namespace Web::Layout { + +BlockBox::BlockBox(DOM::Document& document, DOM::Node* node, NonnullRefPtr<CSS::StyleProperties> style) + : Box(document, node, move(style)) +{ +} + +BlockBox::BlockBox(DOM::Document& document, DOM::Node* node, CSS::ComputedValues computed_values) + : Box(document, node, move(computed_values)) +{ +} + +BlockBox::~BlockBox() +{ +} + +void BlockBox::paint(PaintContext& context, PaintPhase phase) +{ + if (!is_visible()) + return; + + Box::paint(context, phase); + + if (!children_are_inline()) + return; + + for (auto& line_box : m_line_boxes) { + for (auto& fragment : line_box.fragments()) { + if (context.should_show_line_box_borders()) + context.painter().draw_rect(enclosing_int_rect(fragment.absolute_rect()), Color::Green); + fragment.paint(context, phase); + } + } + + // FIXME: Merge this loop with the above somehow.. + if (phase == PaintPhase::FocusOutline) { + for (auto& line_box : m_line_boxes) { + for (auto& fragment : line_box.fragments()) { + auto* node = fragment.layout_node().dom_node(); + if (!node) + continue; + auto* parent = node->parent_element(); + if (!parent) + continue; + if (parent->is_focused()) + context.painter().draw_rect(enclosing_int_rect(fragment.absolute_rect()), context.palette().focus_outline()); + } + } + } +} + +HitTestResult BlockBox::hit_test(const Gfx::IntPoint& position, HitTestType type) const +{ + if (!children_are_inline()) + return Box::hit_test(position, type); + + HitTestResult last_good_candidate; + for (auto& line_box : m_line_boxes) { + for (auto& fragment : line_box.fragments()) { + if (is<Box>(fragment.layout_node()) && downcast<Box>(fragment.layout_node()).stacking_context()) + continue; + if (enclosing_int_rect(fragment.absolute_rect()).contains(position)) { + if (is<BlockBox>(fragment.layout_node())) + return downcast<BlockBox>(fragment.layout_node()).hit_test(position, type); + return { fragment.layout_node(), fragment.text_index_at(position.x()) }; + } + if (fragment.absolute_rect().top() <= position.y()) + last_good_candidate = { fragment.layout_node(), fragment.text_index_at(position.x()) }; + } + } + + if (type == HitTestType::TextCursor && last_good_candidate.layout_node) + return last_good_candidate; + return { absolute_rect().contains(position.x(), position.y()) ? this : nullptr }; +} + +void BlockBox::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode) +{ + auto& containing_block = context.containing_block(); + auto* line_box = &containing_block.ensure_last_line_box(); + + context.dimension_box_on_line(*this, layout_mode); + + float available_width = context.available_width_at_line(containing_block.line_boxes().size() - 1); + + if (layout_mode == LayoutMode::AllPossibleLineBreaks && line_box->width() > 0) { + line_box = &containing_block.add_line_box(); + } else if (layout_mode == LayoutMode::Default && line_box->width() > 0 && line_box->width() + border_box_width() > available_width) { + line_box = &containing_block.add_line_box(); + } + line_box->add_fragment(*this, 0, 0, border_box_width(), height()); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/BlockBox.h b/Userland/Libraries/LibWeb/Layout/BlockBox.h new file mode 100644 index 0000000000..0062841454 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/BlockBox.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018-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/Layout/Box.h> +#include <LibWeb/Layout/LineBox.h> + +namespace Web::Layout { + +class BlockBox : public Box { +public: + BlockBox(DOM::Document&, DOM::Node*, NonnullRefPtr<CSS::StyleProperties>); + BlockBox(DOM::Document&, DOM::Node*, CSS::ComputedValues); + virtual ~BlockBox() override; + + virtual void paint(PaintContext&, PaintPhase) override; + + virtual HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const override; + + BlockBox* previous_sibling() { return downcast<BlockBox>(Node::previous_sibling()); } + const BlockBox* previous_sibling() const { return downcast<BlockBox>(Node::previous_sibling()); } + BlockBox* next_sibling() { return downcast<BlockBox>(Node::next_sibling()); } + const BlockBox* next_sibling() const { return downcast<BlockBox>(Node::next_sibling()); } + + template<typename Callback> + void for_each_fragment(Callback); + template<typename Callback> + void for_each_fragment(Callback) const; + + virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override; +}; + +template<typename Callback> +void BlockBox::for_each_fragment(Callback callback) +{ + for (auto& line_box : line_boxes()) { + for (auto& fragment : line_box.fragments()) { + if (callback(fragment) == IterationDecision::Break) + return; + } + } +} + +template<typename Callback> +void BlockBox::for_each_fragment(Callback callback) const +{ + for (auto& line_box : line_boxes()) { + for (auto& fragment : line_box.fragments()) { + if (callback(fragment) == IterationDecision::Break) + return; + } + } +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp new file mode 100644 index 0000000000..f53ed61c3a --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp @@ -0,0 +1,566 @@ +/* + * 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 <LibWeb/CSS/Length.h> +#include <LibWeb/DOM/Node.h> +#include <LibWeb/Layout/BlockBox.h> +#include <LibWeb/Layout/BlockFormattingContext.h> +#include <LibWeb/Layout/Box.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> +#include <LibWeb/Layout/InlineFormattingContext.h> +#include <LibWeb/Layout/ListItemBox.h> +#include <LibWeb/Layout/WidgetBox.h> +#include <LibWeb/Page/Frame.h> + +namespace Web::Layout { + +BlockFormattingContext::BlockFormattingContext(Box& context_box, FormattingContext* parent) + : FormattingContext(context_box, parent) +{ +} + +BlockFormattingContext::~BlockFormattingContext() +{ +} + +bool BlockFormattingContext::is_initial() const +{ + return is<InitialContainingBlockBox>(context_box()); +} + +void BlockFormattingContext::run(Box& box, LayoutMode layout_mode) +{ + if (is_initial()) { + layout_initial_containing_block(layout_mode); + return; + } + + // FIXME: BFC currently computes the width+height of the target box. + // This is necessary to be able to place absolutely positioned descendants. + // The same work is also done by the parent BFC for each of its blocks.. + + if (layout_mode == LayoutMode::Default) + compute_width(box); + + if (box.children_are_inline()) { + layout_inline_children(box, layout_mode); + } else { + layout_block_level_children(box, layout_mode); + } + + if (layout_mode == LayoutMode::Default) { + compute_height(box); + + box.for_each_child_of_type<Box>([&](auto& child_box) { + if (child_box.is_absolutely_positioned()) { + layout_absolutely_positioned_element(child_box); + } + return IterationDecision::Continue; + }); + } +} + +void BlockFormattingContext::compute_width(Box& box) +{ + if (box.is_absolutely_positioned()) { + compute_width_for_absolutely_positioned_element(box); + return; + } + + if (is<ReplacedBox>(box)) { + // FIXME: This should not be done *by* ReplacedBox + auto& replaced = downcast<ReplacedBox>(box); + replaced.prepare_for_replaced_layout(); + compute_width_for_block_level_replaced_element_in_normal_flow(replaced); + return; + } + + if (box.is_floating()) { + compute_width_for_floating_box(box); + return; + } + + auto& computed_values = box.computed_values(); + float width_of_containing_block = box.width_of_logical_containing_block(); + + auto zero_value = CSS::Length::make_px(0); + + auto margin_left = CSS::Length::make_auto(); + auto margin_right = CSS::Length::make_auto(); + const auto padding_left = computed_values.padding().left.resolved_or_zero(box, width_of_containing_block); + const auto padding_right = computed_values.padding().right.resolved_or_zero(box, width_of_containing_block); + + auto try_compute_width = [&](const auto& a_width) { + CSS::Length width = a_width; + margin_left = computed_values.margin().left.resolved_or_zero(box, width_of_containing_block); + margin_right = computed_values.margin().right.resolved_or_zero(box, width_of_containing_block); + + float total_px = computed_values.border_left().width + computed_values.border_right().width; + for (auto& value : { margin_left, padding_left, width, padding_right, margin_right }) { + total_px += value.to_px(box); + } + + if (!box.is_inline()) { + // 10.3.3 Block-level, non-replaced elements in normal flow + // If 'width' is not 'auto' and 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' (plus any of 'margin-left' or 'margin-right' that are not 'auto') is larger than the width of the containing block, then any 'auto' values for 'margin-left' or 'margin-right' are, for the following rules, treated as zero. + if (width.is_auto() && total_px > width_of_containing_block) { + if (margin_left.is_auto()) + margin_left = zero_value; + if (margin_right.is_auto()) + margin_right = zero_value; + } + + // 10.3.3 cont'd. + auto underflow_px = width_of_containing_block - total_px; + + if (width.is_auto()) { + if (margin_left.is_auto()) + margin_left = zero_value; + if (margin_right.is_auto()) + margin_right = zero_value; + if (underflow_px >= 0) { + width = CSS::Length(underflow_px, CSS::Length::Type::Px); + } else { + width = zero_value; + margin_right = CSS::Length(margin_right.to_px(box) + underflow_px, CSS::Length::Type::Px); + } + } else { + if (!margin_left.is_auto() && !margin_right.is_auto()) { + margin_right = CSS::Length(margin_right.to_px(box) + underflow_px, CSS::Length::Type::Px); + } else if (!margin_left.is_auto() && margin_right.is_auto()) { + margin_right = CSS::Length(underflow_px, CSS::Length::Type::Px); + } else if (margin_left.is_auto() && !margin_right.is_auto()) { + margin_left = CSS::Length(underflow_px, CSS::Length::Type::Px); + } else { // margin_left.is_auto() && margin_right.is_auto() + auto half_of_the_underflow = CSS::Length(underflow_px / 2, CSS::Length::Type::Px); + margin_left = half_of_the_underflow; + margin_right = half_of_the_underflow; + } + } + } else if (box.is_inline_block()) { + + // 10.3.9 'Inline-block', non-replaced elements in normal flow + + // A computed value of 'auto' for 'margin-left' or 'margin-right' becomes a used value of '0'. + if (margin_left.is_auto()) + margin_left = zero_value; + if (margin_right.is_auto()) + margin_right = zero_value; + + // If 'width' is 'auto', the used value is the shrink-to-fit width as for floating elements. + if (width.is_auto()) { + + // Find the available width: in this case, this is the width of the containing + // block minus the used values of 'margin-left', 'border-left-width', 'padding-left', + // 'padding-right', 'border-right-width', 'margin-right', and the widths of any relevant scroll bars. + float available_width = width_of_containing_block + - margin_left.to_px(box) - computed_values.border_left().width - padding_left.to_px(box) + - padding_right.to_px(box) - computed_values.border_right().width - margin_right.to_px(box); + + auto result = calculate_shrink_to_fit_widths(box); + + // Then the shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width). + width = CSS::Length(min(max(result.preferred_minimum_width, available_width), result.preferred_width), CSS::Length::Type::Px); + } + } + + return width; + }; + + auto specified_width = computed_values.width().resolved_or_auto(box, width_of_containing_block); + + // 1. The tentative used width is calculated (without 'min-width' and 'max-width') + auto used_width = try_compute_width(specified_width); + + // 2. The tentative used width is greater than 'max-width', the rules above are applied again, + // but this time using the computed value of 'max-width' as the computed value for 'width'. + auto specified_max_width = computed_values.max_width().resolved_or_auto(box, width_of_containing_block); + if (!specified_max_width.is_auto()) { + if (used_width.to_px(box) > specified_max_width.to_px(box)) { + used_width = try_compute_width(specified_max_width); + } + } + + // 3. If the resulting width is smaller than 'min-width', the rules above are applied again, + // but this time using the value of 'min-width' as the computed value for 'width'. + auto specified_min_width = computed_values.min_width().resolved_or_auto(box, width_of_containing_block); + if (!specified_min_width.is_auto()) { + if (used_width.to_px(box) < specified_min_width.to_px(box)) { + used_width = try_compute_width(specified_min_width); + } + } + + box.set_width(used_width.to_px(box)); + box.box_model().margin.left = margin_left.to_px(box); + box.box_model().margin.right = margin_right.to_px(box); + box.box_model().border.left = computed_values.border_left().width; + box.box_model().border.right = computed_values.border_right().width; + box.box_model().padding.left = padding_left.to_px(box); + box.box_model().padding.right = padding_right.to_px(box); +} + +void BlockFormattingContext::compute_width_for_floating_box(Box& box) +{ + // 10.3.5 Floating, non-replaced elements + auto& computed_values = box.computed_values(); + float width_of_containing_block = box.width_of_logical_containing_block(); + auto zero_value = CSS::Length::make_px(0); + + auto margin_left = CSS::Length::make_auto(); + auto margin_right = CSS::Length::make_auto(); + const auto padding_left = computed_values.padding().left.resolved_or_zero(box, width_of_containing_block); + const auto padding_right = computed_values.padding().right.resolved_or_zero(box, width_of_containing_block); + + // If 'margin-left', or 'margin-right' are computed as 'auto', their used value is '0'. + if (margin_left.is_auto()) + margin_left = zero_value; + if (margin_right.is_auto()) + margin_right = zero_value; + + auto width = computed_values.width().resolved_or_auto(box, width_of_containing_block); + + // If 'width' is computed as 'auto', the used value is the "shrink-to-fit" width. + if (width.is_auto()) { + + // Find the available width: in this case, this is the width of the containing + // block minus the used values of 'margin-left', 'border-left-width', 'padding-left', + // 'padding-right', 'border-right-width', 'margin-right', and the widths of any relevant scroll bars. + float available_width = width_of_containing_block + - margin_left.to_px(box) - computed_values.border_left().width - padding_left.to_px(box) + - padding_right.to_px(box) - computed_values.border_right().width - margin_right.to_px(box); + + auto result = calculate_shrink_to_fit_widths(box); + + // Then the shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width). + width = CSS::Length(min(max(result.preferred_minimum_width, available_width), result.preferred_width), CSS::Length::Type::Px); + } + + float final_width = width.resolved_or_zero(box, width_of_containing_block).to_px(box); + box.set_width(final_width); +} + +void BlockFormattingContext::compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox& box) +{ + box.set_width(compute_width_for_replaced_element(box)); +} + +void BlockFormattingContext::compute_height_for_block_level_replaced_element_in_normal_flow(ReplacedBox& box) +{ + box.set_height(compute_height_for_replaced_element(box)); +} + +void BlockFormattingContext::compute_height(Box& box) +{ + if (is<ReplacedBox>(box)) { + compute_height_for_block_level_replaced_element_in_normal_flow(downcast<ReplacedBox>(box)); + return; + } + + auto& computed_values = box.computed_values(); + auto& containing_block = *box.containing_block(); + + CSS::Length specified_height; + + if (computed_values.height().is_percentage() && !containing_block.computed_values().height().is_absolute()) { + specified_height = CSS::Length::make_auto(); + } else { + specified_height = computed_values.height().resolved_or_auto(box, containing_block.height()); + } + + auto specified_max_height = computed_values.max_height().resolved_or_auto(box, containing_block.height()); + + box.box_model().margin.top = computed_values.margin().top.resolved_or_zero(box, containing_block.width()).to_px(box); + box.box_model().margin.bottom = computed_values.margin().bottom.resolved_or_zero(box, containing_block.width()).to_px(box); + box.box_model().border.top = computed_values.border_top().width; + box.box_model().border.bottom = computed_values.border_bottom().width; + box.box_model().padding.top = computed_values.padding().top.resolved_or_zero(box, containing_block.width()).to_px(box); + box.box_model().padding.bottom = computed_values.padding().bottom.resolved_or_zero(box, containing_block.width()).to_px(box); + + if (!specified_height.is_auto()) { + float used_height = specified_height.to_px(box); + if (!specified_max_height.is_auto()) + used_height = min(used_height, specified_max_height.to_px(box)); + box.set_height(used_height); + } +} + +void BlockFormattingContext::layout_inline_children(Box& box, LayoutMode layout_mode) +{ + InlineFormattingContext context(box, this); + context.run(box, layout_mode); +} + +void BlockFormattingContext::layout_block_level_children(Box& box, LayoutMode layout_mode) +{ + float content_height = 0; + float content_width = 0; + + box.for_each_child_of_type<Box>([&](auto& child_box) { + if (child_box.is_absolutely_positioned()) + return IterationDecision::Continue; + + if (child_box.is_floating()) { + layout_floating_child(child_box, box); + return IterationDecision::Continue; + } + + compute_width(child_box); + layout_inside(child_box, layout_mode); + compute_height(child_box); + + if (is<ReplacedBox>(child_box)) + place_block_level_replaced_element_in_normal_flow(child_box, box); + else if (is<BlockBox>(child_box)) + place_block_level_non_replaced_element_in_normal_flow(child_box, box); + + // FIXME: This should be factored differently. It's uncool that we mutate the tree *during* layout! + // Instead, we should generate the marker box during the tree build. + if (is<ListItemBox>(child_box)) + downcast<ListItemBox>(child_box).layout_marker(); + + content_height = max(content_height, child_box.effective_offset().y() + child_box.height() + child_box.box_model().margin_box().bottom); + content_width = max(content_width, downcast<Box>(child_box).width()); + return IterationDecision::Continue; + }); + + if (layout_mode != LayoutMode::Default) { + if (box.computed_values().width().is_undefined() || box.computed_values().width().is_auto()) + box.set_width(content_width); + } + + // FIXME: It's not right to always shrink-wrap the box to the content here. + box.set_height(content_height); +} + +void BlockFormattingContext::place_block_level_replaced_element_in_normal_flow(Box& child_box, Box& containing_block) +{ + ASSERT(!containing_block.is_absolutely_positioned()); + auto& replaced_element_box_model = child_box.box_model(); + + replaced_element_box_model.margin.top = child_box.computed_values().margin().top.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box); + replaced_element_box_model.margin.bottom = child_box.computed_values().margin().bottom.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box); + replaced_element_box_model.border.top = child_box.computed_values().border_top().width; + replaced_element_box_model.border.bottom = child_box.computed_values().border_bottom().width; + replaced_element_box_model.padding.top = child_box.computed_values().padding().top.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box); + replaced_element_box_model.padding.bottom = child_box.computed_values().padding().bottom.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box); + + float x = replaced_element_box_model.margin.left + + replaced_element_box_model.border.left + + replaced_element_box_model.padding.left + + replaced_element_box_model.offset.left; + + float y = replaced_element_box_model.margin_box().top + containing_block.box_model().offset.top; + + child_box.set_offset(x, y); +} + +void BlockFormattingContext::place_block_level_non_replaced_element_in_normal_flow(Box& child_box, Box& containing_block) +{ + auto& box_model = child_box.box_model(); + auto& computed_values = child_box.computed_values(); + + box_model.margin.top = computed_values.margin().top.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box); + box_model.margin.bottom = computed_values.margin().bottom.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box); + box_model.border.top = computed_values.border_top().width; + box_model.border.bottom = computed_values.border_bottom().width; + box_model.padding.top = computed_values.padding().top.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box); + box_model.padding.bottom = computed_values.padding().bottom.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box); + + float x = box_model.margin.left + + box_model.border.left + + box_model.padding.left + + box_model.offset.left; + + if (containing_block.computed_values().text_align() == CSS::TextAlign::LibwebCenter) { + x = (containing_block.width() / 2) - child_box.width() / 2; + } + + float y = box_model.margin_box().top + + box_model.offset.top; + + // NOTE: Empty (0-height) preceding siblings have their margins collapsed with *their* preceding sibling, etc. + float collapsed_bottom_margin_of_preceding_siblings = 0; + + auto* relevant_sibling = child_box.previous_sibling_of_type<Layout::BlockBox>(); + while (relevant_sibling != nullptr) { + if (!relevant_sibling->is_absolutely_positioned() && !relevant_sibling->is_floating()) { + collapsed_bottom_margin_of_preceding_siblings = max(collapsed_bottom_margin_of_preceding_siblings, relevant_sibling->box_model().margin.bottom); + if (relevant_sibling->border_box_height() > 0) + break; + } + relevant_sibling = relevant_sibling->previous_sibling(); + } + + if (relevant_sibling) { + y += relevant_sibling->effective_offset().y() + + relevant_sibling->height() + + relevant_sibling->box_model().border_box().bottom; + + // Collapse top margin with bottom margin of preceding siblings if needed + float my_margin_top = box_model.margin.top; + + if (my_margin_top < 0 || collapsed_bottom_margin_of_preceding_siblings < 0) { + // Negative margins present. + float largest_negative_margin = -min(my_margin_top, collapsed_bottom_margin_of_preceding_siblings); + float largest_positive_margin = (my_margin_top < 0 && collapsed_bottom_margin_of_preceding_siblings < 0) ? 0 : max(my_margin_top, collapsed_bottom_margin_of_preceding_siblings); + float final_margin = largest_positive_margin - largest_negative_margin; + y += final_margin - my_margin_top; + } else if (collapsed_bottom_margin_of_preceding_siblings > my_margin_top) { + // Sibling's margin is larger than mine, adjust so we use sibling's. + y += collapsed_bottom_margin_of_preceding_siblings - my_margin_top; + } + } + + if (child_box.computed_values().clear() == CSS::Clear::Left || child_box.computed_values().clear() == CSS::Clear::Both) { + if (!m_left_floating_boxes.is_empty()) { + float clearance_y = 0; + for (auto* floating_box : m_left_floating_boxes) { + clearance_y = max(clearance_y, floating_box->effective_offset().y() + floating_box->box_model().margin_box().bottom); + } + y = max(y, clearance_y); + m_left_floating_boxes.clear(); + } + } + + if (child_box.computed_values().clear() == CSS::Clear::Right || child_box.computed_values().clear() == CSS::Clear::Both) { + if (!m_right_floating_boxes.is_empty()) { + float clearance_y = 0; + for (auto* floating_box : m_right_floating_boxes) { + clearance_y = max(clearance_y, floating_box->effective_offset().y() + floating_box->box_model().margin_box().bottom); + } + y = max(y, clearance_y); + m_right_floating_boxes.clear(); + } + } + + child_box.set_offset(x, y); +} + +void BlockFormattingContext::layout_initial_containing_block(LayoutMode layout_mode) +{ + auto viewport_rect = context_box().frame().viewport_rect(); + + auto& icb = downcast<Layout::InitialContainingBlockBox>(context_box()); + icb.build_stacking_context_tree(); + + icb.set_width(viewport_rect.width()); + + layout_block_level_children(context_box(), layout_mode); + + ASSERT(!icb.children_are_inline()); + + // FIXME: The ICB should have the height of the viewport. + // Instead of auto-sizing the ICB, we should spill into overflow. + float lowest_bottom = 0; + icb.for_each_child_of_type<Box>([&](auto& child) { + lowest_bottom = max(lowest_bottom, child.absolute_rect().bottom()); + }); + + // FIXME: This is a hack and should be managed by an overflow mechanism. + icb.set_height(max(static_cast<float>(viewport_rect.height()), lowest_bottom)); + + // FIXME: This is a total hack. Make sure any GUI::Widgets are moved into place after layout. + // We should stop embedding GUI::Widgets entirely, since that won't work out-of-process. + icb.for_each_in_subtree_of_type<Layout::WidgetBox>([&](auto& widget) { + widget.update_widget(); + return IterationDecision::Continue; + }); +} + +static Gfx::FloatRect rect_in_coordinate_space(const Box& box, const Box& context_box) +{ + Gfx::FloatRect rect { box.effective_offset(), box.size() }; + for (auto* ancestor = box.parent(); ancestor; ancestor = ancestor->parent()) { + if (is<Box>(*ancestor)) { + auto offset = downcast<Box>(*ancestor).effective_offset(); + rect.move_by(offset); + } + if (ancestor == &context_box) + break; + } + return rect; +} + +void BlockFormattingContext::layout_floating_child(Box& box, Box& containing_block) +{ + ASSERT(box.is_floating()); + + compute_width(box); + layout_inside(box, LayoutMode::Default); + compute_height(box); + + // First we place the box normally (to get the right y coordinate.) + place_block_level_non_replaced_element_in_normal_flow(box, containing_block); + + // Then we float it to the left or right. + float x = box.effective_offset().x(); + + auto box_in_context_rect = rect_in_coordinate_space(box, context_box()); + float y_in_context_box = box_in_context_rect.y(); + + // Next, float to the left and/or right + if (box.computed_values().float_() == CSS::Float::Left) { + if (!m_left_floating_boxes.is_empty()) { + auto& previous_floating_box = *m_left_floating_boxes.last(); + auto previous_rect = rect_in_coordinate_space(previous_floating_box, context_box()); + if (previous_rect.contains_vertically(y_in_context_box)) { + // This box touches another already floating box. Stack to the right. + x = previous_floating_box.effective_offset().x() + previous_floating_box.width(); + } else { + // This box does not touch another floating box, go all the way to the left. + x = 0; + // Also, forget all previous left-floating boxes while we're here since they're no longer relevant. + m_left_floating_boxes.clear(); + } + } else { + // This is the first left-floating box. Go all the way to the left. + x = 0; + } + m_left_floating_boxes.append(&box); + } else if (box.computed_values().float_() == CSS::Float::Right) { + if (!m_right_floating_boxes.is_empty()) { + auto& previous_floating_box = *m_right_floating_boxes.last(); + auto previous_rect = rect_in_coordinate_space(previous_floating_box, context_box()); + if (previous_rect.contains_vertically(y_in_context_box)) { + // This box touches another already floating box. Stack to the left. + x = previous_floating_box.effective_offset().x() - box.width(); + } else { + // This box does not touch another floating box, go all the way to the right. + x = containing_block.width() - box.width(); + // Also, forget all previous right-floating boxes while we're here since they're no longer relevant. + m_right_floating_boxes.clear(); + } + } else { + // This is the first right-floating box. Go all the way to the right. + x = containing_block.width() - box.width(); + } + m_right_floating_boxes.append(&box); + } + + box.set_offset(x, box.effective_offset().y()); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h new file mode 100644 index 0000000000..efaa9ab0b7 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h @@ -0,0 +1,73 @@ +/* + * 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 <AK/Vector.h> +#include <LibWeb/Forward.h> +#include <LibWeb/Layout/FormattingContext.h> + +namespace Web::Layout { + +class BlockFormattingContext : public FormattingContext { +public: + explicit BlockFormattingContext(Box&, FormattingContext* parent); + ~BlockFormattingContext(); + + virtual void run(Box&, LayoutMode) override; + + bool is_initial() const; + + const Vector<Box*>& left_floating_boxes() const { return m_left_floating_boxes; } + const Vector<Box*>& right_floating_boxes() const { return m_right_floating_boxes; } + +protected: + void compute_width(Box&); + void compute_height(Box&); + +private: + virtual bool is_block_formatting_context() const final { return true; } + + void compute_width_for_floating_box(Box&); + + void compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox&); + void compute_height_for_block_level_replaced_element_in_normal_flow(ReplacedBox&); + + void layout_initial_containing_block(LayoutMode); + + void layout_block_level_children(Box&, LayoutMode); + void layout_inline_children(Box&, LayoutMode); + + void place_block_level_replaced_element_in_normal_flow(Box& child, Box& container); + void place_block_level_non_replaced_element_in_normal_flow(Box& child, Box& container); + + void layout_floating_child(Box&, Box& containing_block); + + Vector<Box*> m_left_floating_boxes; + Vector<Box*> m_right_floating_boxes; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/Box.cpp b/Userland/Libraries/LibWeb/Layout/Box.cpp new file mode 100644 index 0000000000..9c468252a8 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/Box.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2018-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 <LibGUI/Painter.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/HTML/HTMLBodyElement.h> +#include <LibWeb/Layout/BlockBox.h> +#include <LibWeb/Layout/Box.h> +#include <LibWeb/Page/Frame.h> +#include <LibWeb/Painting/BorderPainting.h> + +namespace Web::Layout { + +void Box::paint(PaintContext& context, PaintPhase phase) +{ + if (!is_visible()) + return; + + Gfx::PainterStateSaver saver(context.painter()); + if (is_fixed_position()) + context.painter().translate(context.scroll_offset()); + + Gfx::FloatRect padded_rect; + padded_rect.set_x(absolute_x() - box_model().padding.left); + padded_rect.set_width(width() + box_model().padding.left + box_model().padding.right); + padded_rect.set_y(absolute_y() - box_model().padding.top); + padded_rect.set_height(height() + box_model().padding.top + box_model().padding.bottom); + + if (phase == PaintPhase::Background && !is_body()) { + context.painter().fill_rect(enclosing_int_rect(padded_rect), computed_values().background_color()); + + if (background_image() && background_image()->bitmap()) + context.painter().draw_tiled_bitmap(enclosing_int_rect(padded_rect), *background_image()->bitmap()); + } + + if (phase == PaintPhase::Border) { + Gfx::FloatRect bordered_rect; + bordered_rect.set_x(padded_rect.x() - box_model().border.left); + bordered_rect.set_width(padded_rect.width() + box_model().border.left + box_model().border.right); + bordered_rect.set_y(padded_rect.y() - box_model().border.top); + bordered_rect.set_height(padded_rect.height() + box_model().border.top + box_model().border.bottom); + + Painting::paint_border(context, Painting::BorderEdge::Left, bordered_rect, computed_values()); + Painting::paint_border(context, Painting::BorderEdge::Right, bordered_rect, computed_values()); + Painting::paint_border(context, Painting::BorderEdge::Top, bordered_rect, computed_values()); + Painting::paint_border(context, Painting::BorderEdge::Bottom, bordered_rect, computed_values()); + } + + Layout::NodeWithStyleAndBoxModelMetrics::paint(context, phase); + + if (phase == PaintPhase::Overlay && dom_node() && document().inspected_node() == dom_node()) { + auto content_rect = absolute_rect(); + + auto margin_box = box_model().margin_box(); + Gfx::FloatRect margin_rect; + margin_rect.set_x(absolute_x() - margin_box.left); + margin_rect.set_width(width() + margin_box.left + margin_box.right); + margin_rect.set_y(absolute_y() - margin_box.top); + margin_rect.set_height(height() + margin_box.top + margin_box.bottom); + + context.painter().draw_rect(enclosing_int_rect(margin_rect), Color::Yellow); + context.painter().draw_rect(enclosing_int_rect(padded_rect), Color::Cyan); + context.painter().draw_rect(enclosing_int_rect(content_rect), Color::Magenta); + } + + if (phase == PaintPhase::FocusOutline && dom_node() && dom_node()->is_element() && downcast<DOM::Element>(*dom_node()).is_focused()) { + context.painter().draw_rect(enclosing_int_rect(absolute_rect()), context.palette().focus_outline()); + } +} + +HitTestResult Box::hit_test(const Gfx::IntPoint& position, HitTestType type) const +{ + // FIXME: It would be nice if we could confidently skip over hit testing + // parts of the layout tree, but currently we can't just check + // m_rect.contains() since inline text rects can't be trusted.. + HitTestResult result { absolute_rect().contains(position.x(), position.y()) ? this : nullptr }; + for_each_child_in_paint_order([&](auto& child) { + auto child_result = child.hit_test(position, type); + if (child_result.layout_node) + result = child_result; + }); + return result; +} + +void Box::set_needs_display() +{ + if (!is_inline()) { + frame().set_needs_display(enclosing_int_rect(absolute_rect())); + return; + } + + Node::set_needs_display(); +} + +bool Box::is_body() const +{ + return dom_node() && dom_node() == document().body(); +} + +void Box::set_offset(const Gfx::FloatPoint& offset) +{ + if (m_offset == offset) + return; + m_offset = offset; + did_set_rect(); +} + +void Box::set_size(const Gfx::FloatSize& size) +{ + if (m_size == size) + return; + m_size = size; + did_set_rect(); +} + +Gfx::FloatPoint Box::effective_offset() const +{ + if (m_containing_line_box_fragment) + return m_containing_line_box_fragment->offset(); + return m_offset; +} + +const Gfx::FloatRect Box::absolute_rect() const +{ + Gfx::FloatRect rect { effective_offset(), size() }; + for (auto* block = containing_block(); block; block = block->containing_block()) { + rect.move_by(block->effective_offset()); + } + return rect; +} + +void Box::set_containing_line_box_fragment(LineBoxFragment& fragment) +{ + m_containing_line_box_fragment = fragment.make_weak_ptr(); +} + +StackingContext* Box::enclosing_stacking_context() +{ + for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) { + if (!is<Box>(ancestor)) + continue; + auto& ancestor_box = downcast<Box>(*ancestor); + if (!ancestor_box.establishes_stacking_context()) + continue; + ASSERT(ancestor_box.stacking_context()); + return ancestor_box.stacking_context(); + } + // We should always reach the Layout::InitialContainingBlockBox stacking context. + ASSERT_NOT_REACHED(); +} + +bool Box::establishes_stacking_context() const +{ + if (!has_style()) + return false; + if (dom_node() == document().root()) + return true; + auto position = computed_values().position(); + auto z_index = computed_values().z_index(); + if (position == CSS::Position::Absolute || position == CSS::Position::Relative) { + if (z_index.has_value()) + return true; + } + if (position == CSS::Position::Fixed || position == CSS::Position::Sticky) + return true; + return false; +} + +LineBox& Box::ensure_last_line_box() +{ + if (m_line_boxes.is_empty()) + return add_line_box(); + return m_line_boxes.last(); +} + +LineBox& Box::add_line_box() +{ + m_line_boxes.append(LineBox()); + return m_line_boxes.last(); +} + +float Box::width_of_logical_containing_block() const +{ + auto* containing_block = this->containing_block(); + ASSERT(containing_block); + return containing_block->width(); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/Box.h b/Userland/Libraries/LibWeb/Layout/Box.h new file mode 100644 index 0000000000..7b05b381e3 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/Box.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2018-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 <AK/OwnPtr.h> +#include <LibGfx/Rect.h> +#include <LibWeb/Layout/LineBox.h> +#include <LibWeb/Layout/Node.h> +#include <LibWeb/Painting/StackingContext.h> + +namespace Web::Layout { + +class Box : public NodeWithStyleAndBoxModelMetrics { +public: + const Gfx::FloatRect absolute_rect() const; + + Gfx::FloatPoint effective_offset() const; + + void set_offset(const Gfx::FloatPoint& offset); + void set_offset(float x, float y) { set_offset({ x, y }); } + + const Gfx::FloatSize& size() const { return m_size; } + void set_size(const Gfx::FloatSize&); + void set_size(float width, float height) { set_size({ width, height }); } + + void set_width(float width) { set_size(width, height()); } + void set_height(float height) { set_size(width(), height); } + float width() const { return m_size.width(); } + float height() const { return m_size.height(); } + + float border_box_width() const + { + auto border_box = box_model().border_box(); + return width() + border_box.left + border_box.right; + } + + float border_box_height() const + { + auto border_box = box_model().border_box(); + return height() + border_box.top + border_box.bottom; + } + + Gfx::FloatRect content_box_as_relative_rect() const + { + return { m_offset, m_size }; + } + + Gfx::FloatRect margin_box_as_relative_rect() const + { + auto rect = content_box_as_relative_rect(); + auto margin_box = box_model().margin_box(); + rect.set_x(rect.x() - margin_box.left); + rect.set_width(rect.width() + margin_box.left + margin_box.right); + rect.set_y(rect.y() - margin_box.top); + rect.set_height(rect.height() + margin_box.top + margin_box.bottom); + return rect; + } + + float absolute_x() const { return absolute_rect().x(); } + float absolute_y() const { return absolute_rect().y(); } + Gfx::FloatPoint absolute_position() const { return absolute_rect().location(); } + + virtual HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const override; + virtual void set_needs_display() override; + + bool is_body() const; + + void set_containing_line_box_fragment(LineBoxFragment&); + + bool establishes_stacking_context() const; + StackingContext* stacking_context() { return m_stacking_context; } + const StackingContext* stacking_context() const { return m_stacking_context; } + void set_stacking_context(NonnullOwnPtr<StackingContext> context) { m_stacking_context = move(context); } + StackingContext* enclosing_stacking_context(); + + virtual void paint(PaintContext&, PaintPhase) override; + + Vector<LineBox>& line_boxes() { return m_line_boxes; } + const Vector<LineBox>& line_boxes() const { return m_line_boxes; } + + LineBox& ensure_last_line_box(); + LineBox& add_line_box(); + + virtual float width_of_logical_containing_block() const; + +protected: + Box(DOM::Document& document, DOM::Node* node, NonnullRefPtr<CSS::StyleProperties> style) + : NodeWithStyleAndBoxModelMetrics(document, node, move(style)) + { + } + + Box(DOM::Document& document, DOM::Node* node, CSS::ComputedValues computed_values) + : NodeWithStyleAndBoxModelMetrics(document, node, move(computed_values)) + { + } + + virtual void did_set_rect() { } + + Vector<LineBox> m_line_boxes; + +private: + virtual bool is_box() const final { return true; } + + Gfx::FloatPoint m_offset; + Gfx::FloatSize m_size; + + // Some boxes hang off of line box fragments. (inline-block, inline-table, replaced, etc) + WeakPtr<LineBoxFragment> m_containing_line_box_fragment; + + OwnPtr<StackingContext> m_stacking_context; +}; + +} + +namespace AK { +template<> +inline bool is<Web::Layout::Box>(const Web::Layout::Node& input) +{ + return input.is_box(); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/BoxModelMetrics.cpp b/Userland/Libraries/LibWeb/Layout/BoxModelMetrics.cpp new file mode 100644 index 0000000000..d18e5ef531 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/BoxModelMetrics.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-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 <LibWeb/Layout/BoxModelMetrics.h> + +namespace Web::Layout { + +PixelBox BoxModelMetrics::margin_box() const +{ + return { + margin.top + border.top + padding.top, + margin.right + border.right + padding.right, + margin.bottom + border.bottom + padding.bottom, + margin.left + border.left + padding.left, + }; +} + +PixelBox BoxModelMetrics::padding_box() const +{ + return { + padding.top, + padding.right, + padding.bottom, + padding.left, + }; +} + +PixelBox BoxModelMetrics::border_box() const +{ + return { + border.top + padding.top, + border.right + padding.right, + border.bottom + padding.bottom, + border.left + padding.left, + }; +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/BoxModelMetrics.h b/Userland/Libraries/LibWeb/Layout/BoxModelMetrics.h new file mode 100644 index 0000000000..4b36062b7b --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/BoxModelMetrics.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-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 <LibGfx/Size.h> + +namespace Web::Layout { + +struct PixelBox { + float top { 0 }; + float right { 0 }; + float bottom { 0 }; + float left { 0 }; +}; + +struct BoxModelMetrics { +public: + PixelBox margin; + PixelBox padding; + PixelBox border; + PixelBox offset; + + PixelBox margin_box() const; + PixelBox padding_box() const; + PixelBox border_box() const; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/BreakNode.cpp b/Userland/Libraries/LibWeb/Layout/BreakNode.cpp new file mode 100644 index 0000000000..9951ae4fe0 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/BreakNode.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-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 <LibWeb/Layout/BlockBox.h> +#include <LibWeb/Layout/BreakNode.h> +#include <LibWeb/Layout/InlineFormattingContext.h> + +namespace Web::Layout { + +BreakNode::BreakNode(DOM::Document& document, HTML::HTMLBRElement& element) + : Layout::NodeWithStyleAndBoxModelMetrics(document, &element, CSS::StyleProperties::create()) +{ + set_inline(true); +} + +BreakNode::~BreakNode() +{ +} + +void BreakNode::split_into_lines(InlineFormattingContext& context, LayoutMode) +{ + context.containing_block().add_line_box(); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/BreakNode.h b/Userland/Libraries/LibWeb/Layout/BreakNode.h new file mode 100644 index 0000000000..58ebbe3322 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/BreakNode.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018-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/HTML/HTMLBRElement.h> +#include <LibWeb/Layout/Node.h> + +namespace Web::Layout { + +class BreakNode final : public NodeWithStyleAndBoxModelMetrics { +public: + BreakNode(DOM::Document&, HTML::HTMLBRElement&); + virtual ~BreakNode() override; + + const HTML::HTMLBRElement& dom_node() const { return downcast<HTML::HTMLBRElement>(*Node::dom_node()); } + +private: + virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp b/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp new file mode 100644 index 0000000000..c0b3706e28 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp @@ -0,0 +1,117 @@ +/* + * 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 <LibGUI/Event.h> +#include <LibGUI/Painter.h> +#include <LibGfx/Font.h> +#include <LibGfx/StylePainter.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/Layout/ButtonBox.h> +#include <LibWeb/Page/Frame.h> + +namespace Web::Layout { + +ButtonBox::ButtonBox(DOM::Document& document, HTML::HTMLInputElement& element, NonnullRefPtr<CSS::StyleProperties> style) + : ReplacedBox(document, element, move(style)) +{ +} + +ButtonBox::~ButtonBox() +{ +} + +void ButtonBox::prepare_for_replaced_layout() +{ + set_intrinsic_width(font().width(dom_node().value()) + 20); + set_has_intrinsic_width(true); + + set_intrinsic_height(20); + set_has_intrinsic_height(true); +} + +void ButtonBox::paint(PaintContext& context, PaintPhase phase) +{ + if (!is_visible()) + return; + + ReplacedBox::paint(context, phase); + + if (phase == PaintPhase::Foreground) { + bool hovered = document().hovered_node() == &dom_node(); + Gfx::StylePainter::paint_button(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), Gfx::ButtonStyle::Normal, m_being_pressed, hovered, dom_node().checked(), dom_node().enabled()); + + auto text_rect = enclosing_int_rect(absolute_rect()); + if (m_being_pressed) + text_rect.move_by(1, 1); + context.painter().draw_text(text_rect, dom_node().value(), font(), Gfx::TextAlignment::Center, context.palette().button_text()); + } +} + +void ButtonBox::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned) +{ + if (button != GUI::MouseButton::Left || !dom_node().enabled()) + return; + + m_being_pressed = true; + set_needs_display(); + + m_tracking_mouse = true; + frame().event_handler().set_mouse_event_tracking_layout_node(this); +} + +void ButtonBox::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned) +{ + if (!m_tracking_mouse || button != GUI::MouseButton::Left || !dom_node().enabled()) + return; + + // NOTE: Handling the click may run arbitrary JS, which could disappear this node. + NonnullRefPtr protected_this = *this; + NonnullRefPtr protected_frame = frame(); + + bool is_inside = enclosing_int_rect(absolute_rect()).contains(position); + if (is_inside) + dom_node().did_click_button({}); + + m_being_pressed = false; + m_tracking_mouse = false; + + protected_frame->event_handler().set_mouse_event_tracking_layout_node(nullptr); +} + +void ButtonBox::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned, unsigned) +{ + if (!m_tracking_mouse || !dom_node().enabled()) + return; + + bool is_inside = enclosing_int_rect(absolute_rect()).contains(position); + if (m_being_pressed == is_inside) + return; + + m_being_pressed = is_inside; + set_needs_display(); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/ButtonBox.h b/Userland/Libraries/LibWeb/Layout/ButtonBox.h new file mode 100644 index 0000000000..f26c7ed8d8 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/ButtonBox.h @@ -0,0 +1,55 @@ +/* + * 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/HTML/HTMLInputElement.h> +#include <LibWeb/Layout/ReplacedBox.h> + +namespace Web::Layout { + +class ButtonBox : public ReplacedBox { +public: + ButtonBox(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>); + virtual ~ButtonBox() override; + + virtual void prepare_for_replaced_layout() override; + virtual void paint(PaintContext&, PaintPhase) override; + + const HTML::HTMLInputElement& dom_node() const { return static_cast<const HTML::HTMLInputElement&>(ReplacedBox::dom_node()); } + HTML::HTMLInputElement& dom_node() { return static_cast<HTML::HTMLInputElement&>(ReplacedBox::dom_node()); } + +private: + virtual bool wants_mouse_events() const override { return true; } + virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; + virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; + virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers) override; + + bool m_being_pressed { false }; + bool m_tracking_mouse { false }; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/CanvasBox.cpp b/Userland/Libraries/LibWeb/Layout/CanvasBox.cpp new file mode 100644 index 0000000000..c1eb048ace --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/CanvasBox.cpp @@ -0,0 +1,68 @@ +/* + * 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 <LibGUI/Painter.h> +#include <LibGfx/Font.h> +#include <LibGfx/StylePainter.h> +#include <LibWeb/Layout/CanvasBox.h> + +namespace Web::Layout { + +CanvasBox::CanvasBox(DOM::Document& document, HTML::HTMLCanvasElement& element, NonnullRefPtr<CSS::StyleProperties> style) + : ReplacedBox(document, element, move(style)) +{ +} + +CanvasBox::~CanvasBox() +{ +} + +void CanvasBox::prepare_for_replaced_layout() +{ + set_has_intrinsic_width(true); + set_has_intrinsic_height(true); + set_intrinsic_width(dom_node().width()); + set_intrinsic_height(dom_node().height()); +} + +void CanvasBox::paint(PaintContext& context, PaintPhase phase) +{ + if (!is_visible()) + return; + + ReplacedBox::paint(context, phase); + + if (phase == PaintPhase::Foreground) { + // FIXME: This should be done at a different level. Also rect() does not include padding etc! + if (!context.viewport_rect().intersects(enclosing_int_rect(absolute_rect()))) + return; + + if (dom_node().bitmap()) + context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *dom_node().bitmap(), dom_node().bitmap()->rect()); + } +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/CanvasBox.h b/Userland/Libraries/LibWeb/Layout/CanvasBox.h new file mode 100644 index 0000000000..150e28ae21 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/CanvasBox.h @@ -0,0 +1,45 @@ +/* + * 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/HTML/HTMLCanvasElement.h> +#include <LibWeb/Layout/ReplacedBox.h> + +namespace Web::Layout { + +class CanvasBox : public ReplacedBox { +public: + CanvasBox(DOM::Document&, HTML::HTMLCanvasElement&, NonnullRefPtr<CSS::StyleProperties>); + virtual ~CanvasBox() override; + + virtual void prepare_for_replaced_layout() override; + virtual void paint(PaintContext&, PaintPhase) override; + + const HTML::HTMLCanvasElement& dom_node() const { return static_cast<const HTML::HTMLCanvasElement&>(ReplacedBox::dom_node()); } +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/CheckBox.cpp b/Userland/Libraries/LibWeb/Layout/CheckBox.cpp new file mode 100644 index 0000000000..a259e4f2d5 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/CheckBox.cpp @@ -0,0 +1,103 @@ +/* + * 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 <LibGUI/Event.h> +#include <LibGUI/Painter.h> +#include <LibGfx/Font.h> +#include <LibGfx/StylePainter.h> +#include <LibWeb/Layout/CheckBox.h> +#include <LibWeb/Page/Frame.h> + +namespace Web::Layout { + +CheckBox::CheckBox(DOM::Document& document, HTML::HTMLInputElement& element, NonnullRefPtr<CSS::StyleProperties> style) + : ReplacedBox(document, element, move(style)) +{ + set_has_intrinsic_width(true); + set_has_intrinsic_height(true); + set_intrinsic_width(13); + set_intrinsic_height(13); +} + +CheckBox::~CheckBox() +{ +} + +void CheckBox::paint(PaintContext& context, PaintPhase phase) +{ + if (!is_visible()) + return; + + ReplacedBox::paint(context, phase); + + if (phase == PaintPhase::Foreground) { + Gfx::StylePainter::paint_check_box(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), dom_node().enabled(), dom_node().checked(), m_being_pressed); + } +} + +void CheckBox::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned) +{ + if (button != GUI::MouseButton::Left || !dom_node().enabled()) + return; + + m_being_pressed = true; + set_needs_display(); + + m_tracking_mouse = true; + frame().event_handler().set_mouse_event_tracking_layout_node(this); +} + +void CheckBox::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned) +{ + if (!m_tracking_mouse || button != GUI::MouseButton::Left || !dom_node().enabled()) + return; + + // NOTE: Changing the checked state of the DOM node may run arbitrary JS, which could disappear this node. + NonnullRefPtr protect = *this; + + bool is_inside = enclosing_int_rect(absolute_rect()).contains(position); + if (is_inside) + dom_node().set_checked(!dom_node().checked()); + + m_being_pressed = false; + m_tracking_mouse = false; + frame().event_handler().set_mouse_event_tracking_layout_node(nullptr); +} + +void CheckBox::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned, unsigned) +{ + if (!m_tracking_mouse || !dom_node().enabled()) + return; + + bool is_inside = enclosing_int_rect(absolute_rect()).contains(position); + if (m_being_pressed == is_inside) + return; + + m_being_pressed = is_inside; + set_needs_display(); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/CheckBox.h b/Userland/Libraries/LibWeb/Layout/CheckBox.h new file mode 100644 index 0000000000..47809c07f9 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/CheckBox.h @@ -0,0 +1,54 @@ +/* + * 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/HTML/HTMLInputElement.h> +#include <LibWeb/Layout/ReplacedBox.h> + +namespace Web::Layout { + +class CheckBox : public ReplacedBox { +public: + CheckBox(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>); + virtual ~CheckBox() override; + + virtual void paint(PaintContext&, PaintPhase) override; + + const HTML::HTMLInputElement& dom_node() const { return static_cast<const HTML::HTMLInputElement&>(ReplacedBox::dom_node()); } + HTML::HTMLInputElement& dom_node() { return static_cast<HTML::HTMLInputElement&>(ReplacedBox::dom_node()); } + +private: + virtual bool wants_mouse_events() const override { return true; } + virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; + virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; + virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers) override; + + bool m_being_pressed { false }; + bool m_tracking_mouse { false }; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp new file mode 100644 index 0000000000..e0a802e002 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp @@ -0,0 +1,548 @@ +/* + * 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 <LibWeb/Dump.h> +#include <LibWeb/Layout/BlockBox.h> +#include <LibWeb/Layout/BlockFormattingContext.h> +#include <LibWeb/Layout/Box.h> +#include <LibWeb/Layout/FormattingContext.h> +#include <LibWeb/Layout/InlineFormattingContext.h> +#include <LibWeb/Layout/ReplacedBox.h> +#include <LibWeb/Layout/TableBox.h> +#include <LibWeb/Layout/TableCellBox.h> +#include <LibWeb/Layout/TableFormattingContext.h> +#include <LibWeb/Layout/TableRowBox.h> + +namespace Web::Layout { + +FormattingContext::FormattingContext(Box& context_box, FormattingContext* parent) + : m_parent(parent) + , m_context_box(&context_box) +{ +} + +FormattingContext::~FormattingContext() +{ +} + +bool FormattingContext::creates_block_formatting_context(const Box& box) +{ + if (box.is_root_element()) + return true; + if (box.is_floating()) + return true; + if (box.is_absolutely_positioned()) + return true; + if (box.is_inline_block()) + return true; + if (is<TableCellBox>(box)) + return true; + // FIXME: table-caption + // FIXME: anonymous table cells + // FIXME: Block elements where overflow has a value other than visible and clip. + // FIXME: display: flow-root + // FIXME: Elements with contain: layout, content, or paint. + // FIXME: flex + // FIXME: grid + // FIXME: multicol + // FIXME: column-span: all + return false; +} + +void FormattingContext::layout_inside(Box& box, LayoutMode layout_mode) +{ + if (creates_block_formatting_context(box)) { + BlockFormattingContext context(box, this); + context.run(box, layout_mode); + return; + } + if (is<TableBox>(box)) { + TableFormattingContext context(box, this); + context.run(box, layout_mode); + } else if (box.children_are_inline()) { + InlineFormattingContext context(box, this); + context.run(box, layout_mode); + } else { + // FIXME: This needs refactoring! + ASSERT(is_block_formatting_context()); + run(box, layout_mode); + } +} + +static float greatest_child_width(const Box& box) +{ + float max_width = 0; + if (box.children_are_inline()) { + for (auto& child : box.line_boxes()) { + max_width = max(max_width, child.width()); + } + } else { + box.for_each_child_of_type<Box>([&](auto& child) { + max_width = max(max_width, child.border_box_width()); + }); + } + return max_width; +} + +FormattingContext::ShrinkToFitResult FormattingContext::calculate_shrink_to_fit_widths(Box& box) +{ + // Calculate the preferred width by formatting the content without breaking lines + // other than where explicit line breaks occur. + layout_inside(box, LayoutMode::OnlyRequiredLineBreaks); + float preferred_width = greatest_child_width(box); + + // Also calculate the preferred minimum width, e.g., by trying all possible line breaks. + // CSS 2.2 does not define the exact algorithm. + + layout_inside(box, LayoutMode::AllPossibleLineBreaks); + float preferred_minimum_width = greatest_child_width(box); + + return { preferred_width, preferred_minimum_width }; +} + +static Gfx::FloatSize solve_replaced_size_constraint(float w, float h, const ReplacedBox& box) +{ + // 10.4 Minimum and maximum widths: 'min-width' and 'max-width' + + auto& containing_block = *box.containing_block(); + auto specified_min_width = box.computed_values().min_width().resolved_or_zero(box, containing_block.width()).to_px(box); + auto specified_max_width = box.computed_values().max_width().resolved(CSS::Length::make_px(w), box, containing_block.width()).to_px(box); + auto specified_min_height = box.computed_values().min_height().resolved_or_auto(box, containing_block.height()).to_px(box); + auto specified_max_height = box.computed_values().max_height().resolved(CSS::Length::make_px(h), box, containing_block.height()).to_px(box); + + auto min_width = min(specified_min_width, specified_max_width); + auto max_width = max(specified_min_width, specified_max_width); + auto min_height = min(specified_min_height, specified_max_height); + auto max_height = max(specified_min_height, specified_max_height); + + if (w > max_width) + return { w, max(max_width * h / w, min_height) }; + if (w < min_width) + return { max_width, min(min_width * h / w, max_height) }; + if (h > max_height) + return { max(max_height * w / h, min_width), max_height }; + if (h < min_height) + return { min(min_height * w / h, max_width), min_height }; + if ((w > max_width && h > max_height) && (max_width / w < max_height / h)) + return { max_width, max(min_height, max_width * h / w) }; + if ((w > max_width && h > max_height) && (max_width / w > max_height / h)) + return { max(min_width, max_height * w / h), max_height }; + if ((w < min_width && h < min_height) && (min_width / w < min_height / h)) + return { min(max_width, min_height * w / h), min_height }; + if ((w < min_width && h < min_height) && (min_width / w > min_height / h)) + return { min_width, min(max_height, min_width * h / w) }; + if (w < min_width && h > max_height) + return { min_width, max_height }; + if (w > max_width && h < min_height) + return { max_width, min_height }; + return { w, h }; +} + +float FormattingContext::tentative_width_for_replaced_element(const ReplacedBox& box, const CSS::Length& width) +{ + auto& containing_block = *box.containing_block(); + auto specified_height = box.computed_values().height().resolved_or_auto(box, containing_block.height()); + + float used_width = width.to_px(box); + + // If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic width, + // then that intrinsic width is the used value of 'width'. + if (specified_height.is_auto() && width.is_auto() && box.has_intrinsic_width()) { + used_width = box.intrinsic_width(); + } + + // If 'height' and 'width' both have computed values of 'auto' and the element has no intrinsic width, + // but does have an intrinsic height and intrinsic ratio; + // or if 'width' has a computed value of 'auto', + // 'height' has some other computed value, and the element does have an intrinsic ratio; then the used value of 'width' is: + // + // (used height) * (intrinsic ratio) + else if ((specified_height.is_auto() && width.is_auto() && !box.has_intrinsic_width() && box.has_intrinsic_height() && box.has_intrinsic_ratio()) || (width.is_auto() && box.has_intrinsic_ratio())) { + used_width = compute_height_for_replaced_element(box) * box.intrinsic_ratio(); + } + + else if (width.is_auto() && box.has_intrinsic_width()) { + used_width = box.intrinsic_width(); + } + + else if (width.is_auto()) { + used_width = 300; + } + + return used_width; +} + +void FormattingContext::compute_width_for_absolutely_positioned_element(Box& box) +{ + if (is<ReplacedBox>(box)) + compute_width_for_absolutely_positioned_replaced_element(downcast<ReplacedBox>(box)); + else + compute_width_for_absolutely_positioned_non_replaced_element(box); +} + +void FormattingContext::compute_height_for_absolutely_positioned_element(Box& box) +{ + if (is<ReplacedBox>(box)) + compute_height_for_absolutely_positioned_replaced_element(downcast<ReplacedBox>(box)); + else + compute_height_for_absolutely_positioned_non_replaced_element(box); +} + +float FormattingContext::compute_width_for_replaced_element(const ReplacedBox& box) +{ + // 10.3.4 Block-level, replaced elements in normal flow... + // 10.3.2 Inline, replaced elements + + auto zero_value = CSS::Length::make_px(0); + auto& containing_block = *box.containing_block(); + + auto margin_left = box.computed_values().margin().left.resolved_or_zero(box, containing_block.width()); + auto margin_right = box.computed_values().margin().right.resolved_or_zero(box, containing_block.width()); + + // A computed value of 'auto' for 'margin-left' or 'margin-right' becomes a used value of '0'. + if (margin_left.is_auto()) + margin_left = zero_value; + if (margin_right.is_auto()) + margin_right = zero_value; + + auto specified_width = box.computed_values().width().resolved_or_auto(box, containing_block.width()); + + // 1. The tentative used width is calculated (without 'min-width' and 'max-width') + auto used_width = tentative_width_for_replaced_element(box, specified_width); + + // 2. The tentative used width is greater than 'max-width', the rules above are applied again, + // but this time using the computed value of 'max-width' as the computed value for 'width'. + auto specified_max_width = box.computed_values().max_width().resolved_or_auto(box, containing_block.width()); + if (!specified_max_width.is_auto()) { + if (used_width > specified_max_width.to_px(box)) { + used_width = tentative_width_for_replaced_element(box, specified_max_width); + } + } + + // 3. If the resulting width is smaller than 'min-width', the rules above are applied again, + // but this time using the value of 'min-width' as the computed value for 'width'. + auto specified_min_width = box.computed_values().min_width().resolved_or_auto(box, containing_block.width()); + if (!specified_min_width.is_auto()) { + if (used_width < specified_min_width.to_px(box)) { + used_width = tentative_width_for_replaced_element(box, specified_min_width); + } + } + + return used_width; +} + +float FormattingContext::tentative_height_for_replaced_element(const ReplacedBox& box, const CSS::Length& height) +{ + auto& containing_block = *box.containing_block(); + auto specified_width = box.computed_values().width().resolved_or_auto(box, containing_block.width()); + + float used_height = height.to_px(box); + + // If 'height' and 'width' both have computed values of 'auto' and the element also has + // an intrinsic height, then that intrinsic height is the used value of 'height'. + if (specified_width.is_auto() && height.is_auto() && box.has_intrinsic_height()) + used_height = box.intrinsic_height(); + else if (height.is_auto() && box.has_intrinsic_ratio()) + used_height = compute_width_for_replaced_element(box) / box.intrinsic_ratio(); + else if (height.is_auto() && box.has_intrinsic_height()) + used_height = box.intrinsic_height(); + else if (height.is_auto()) + used_height = 150; + + return used_height; +} + +float FormattingContext::compute_height_for_replaced_element(const ReplacedBox& box) +{ + // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, + // 'inline-block' replaced elements in normal flow and floating replaced elements + + auto& containing_block = *box.containing_block(); + auto specified_width = box.computed_values().width().resolved_or_auto(box, containing_block.width()); + auto specified_height = box.computed_values().height().resolved_or_auto(box, containing_block.height()); + + float used_height = tentative_height_for_replaced_element(box, specified_height); + + if (specified_width.is_auto() && specified_height.is_auto() && box.has_intrinsic_ratio()) { + float w = tentative_width_for_replaced_element(box, specified_width); + float h = used_height; + used_height = solve_replaced_size_constraint(w, h, box).height(); + } + + return used_height; +} + +void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_element(Box& box) +{ + auto& containing_block = *box.containing_block(); + auto& computed_values = box.computed_values(); + auto zero_value = CSS::Length::make_px(0); + + auto margin_left = CSS::Length::make_auto(); + auto margin_right = CSS::Length::make_auto(); + const auto border_left = computed_values.border_left().width; + const auto border_right = computed_values.border_right().width; + const auto padding_left = computed_values.padding().left.resolved_or_zero(box, containing_block.width()); + const auto padding_right = computed_values.padding().right.resolved_or_zero(box, containing_block.width()); + + auto try_compute_width = [&](const auto& a_width) { + margin_left = computed_values.margin().left.resolved_or_zero(box, containing_block.width()); + margin_right = computed_values.margin().right.resolved_or_zero(box, containing_block.width()); + + auto left = computed_values.offset().left.resolved_or_auto(box, containing_block.width()); + auto right = computed_values.offset().right.resolved_or_auto(box, containing_block.width()); + auto width = a_width; + + auto solve_for_left = [&] { + return CSS::Length(containing_block.width() - margin_left.to_px(box) - border_left - padding_left.to_px(box) - width.to_px(box) - padding_right.to_px(box) - border_right - margin_right.to_px(box) - right.to_px(box), CSS::Length::Type::Px); + }; + + auto solve_for_width = [&] { + return CSS::Length(containing_block.width() - left.to_px(box) - margin_left.to_px(box) - border_left - padding_left.to_px(box) - padding_right.to_px(box) - border_right - margin_right.to_px(box) - right.to_px(box), CSS::Length::Type::Px); + }; + + auto solve_for_right = [&] { + return CSS::Length(containing_block.width() - left.to_px(box) - margin_left.to_px(box) - border_left - padding_left.to_px(box) - width.to_px(box) - padding_right.to_px(box) - border_right - margin_right.to_px(box), CSS::Length::Type::Px); + }; + + // If all three of 'left', 'width', and 'right' are 'auto': + if (left.is_auto() && width.is_auto() && right.is_auto()) { + // First set any 'auto' values for 'margin-left' and 'margin-right' to 0. + if (margin_left.is_auto()) + margin_left = CSS::Length::make_px(0); + if (margin_right.is_auto()) + margin_right = CSS::Length::make_px(0); + // Then, if the 'direction' property of the element establishing the static-position containing block + // is 'ltr' set 'left' to the static position and apply rule number three below; + // otherwise, set 'right' to the static position and apply rule number one below. + // FIXME: This is very hackish. + left = CSS::Length::make_px(0); + goto Rule3; + } + + if (!left.is_auto() && !width.is_auto() && !right.is_auto()) { + // FIXME: This should be solved in a more complicated way. + return width; + } + + if (margin_left.is_auto()) + margin_left = CSS::Length::make_px(0); + if (margin_right.is_auto()) + margin_right = CSS::Length::make_px(0); + + // 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', + // then the width is shrink-to-fit. Then solve for 'left' + if (left.is_auto() && width.is_auto() && !right.is_auto()) { + auto result = calculate_shrink_to_fit_widths(box); + solve_for_left(); + auto available_width = solve_for_width(); + width = CSS::Length(min(max(result.preferred_minimum_width, available_width.to_px(box)), result.preferred_width), CSS::Length::Type::Px); + } + + // 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', + // then if the 'direction' property of the element establishing + // the static-position containing block is 'ltr' set 'left' + // to the static position, otherwise set 'right' to the static position. + // Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr'). + else if (left.is_auto() && right.is_auto() && !width.is_auto()) { + // FIXME: Check direction + // FIXME: Use the static-position containing block + left = zero_value; + right = solve_for_right(); + } + + // 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', + // then the width is shrink-to-fit. Then solve for 'right' + else if (width.is_auto() && right.is_auto() && !left.is_auto()) { + Rule3: + auto result = calculate_shrink_to_fit_widths(box); + auto available_width = solve_for_width(); + width = CSS::Length(min(max(result.preferred_minimum_width, available_width.to_px(box)), result.preferred_width), CSS::Length::Type::Px); + right = solve_for_right(); + } + + // 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left' + else if (left.is_auto() && !width.is_auto() && !right.is_auto()) { + left = solve_for_left(); + } + + // 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width' + else if (width.is_auto() && !left.is_auto() && !right.is_auto()) { + width = solve_for_width(); + } + + // 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right' + else if (right.is_auto() && !left.is_auto() && !width.is_auto()) { + right = solve_for_right(); + } + + return width; + }; + + auto specified_width = computed_values.width().resolved_or_auto(box, containing_block.width()); + + // 1. The tentative used width is calculated (without 'min-width' and 'max-width') + auto used_width = try_compute_width(specified_width); + + // 2. The tentative used width is greater than 'max-width', the rules above are applied again, + // but this time using the computed value of 'max-width' as the computed value for 'width'. + auto specified_max_width = computed_values.max_width().resolved_or_auto(box, containing_block.width()); + if (!specified_max_width.is_auto()) { + if (used_width.to_px(box) > specified_max_width.to_px(box)) { + used_width = try_compute_width(specified_max_width); + } + } + + // 3. If the resulting width is smaller than 'min-width', the rules above are applied again, + // but this time using the value of 'min-width' as the computed value for 'width'. + auto specified_min_width = computed_values.min_width().resolved_or_auto(box, containing_block.width()); + if (!specified_min_width.is_auto()) { + if (used_width.to_px(box) < specified_min_width.to_px(box)) { + used_width = try_compute_width(specified_min_width); + } + } + + box.set_width(used_width.to_px(box)); + + box.box_model().margin.left = margin_left.to_px(box); + box.box_model().margin.right = margin_right.to_px(box); + box.box_model().border.left = border_left; + box.box_model().border.right = border_right; + box.box_model().padding.left = padding_left.to_px(box); + box.box_model().padding.right = padding_right.to_px(box); +} + +void FormattingContext::compute_width_for_absolutely_positioned_replaced_element(ReplacedBox& box) +{ + // 10.3.8 Absolutely positioned, replaced elements + // The used value of 'width' is determined as for inline replaced elements. + box.prepare_for_replaced_layout(); + box.set_width(compute_width_for_replaced_element(box)); +} + +void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_element(Box& box) +{ + auto& computed_values = box.computed_values(); + auto& containing_block = *box.containing_block(); + + CSS::Length specified_height; + + if (computed_values.height().is_percentage() && !containing_block.computed_values().height().is_absolute()) { + specified_height = CSS::Length::make_auto(); + } else { + specified_height = computed_values.height().resolved_or_auto(box, containing_block.height()); + } + + auto specified_max_height = computed_values.max_height().resolved_or_auto(box, containing_block.height()); + + box.box_model().margin.top = computed_values.margin().top.resolved_or_zero(box, containing_block.width()).to_px(box); + box.box_model().margin.bottom = computed_values.margin().bottom.resolved_or_zero(box, containing_block.width()).to_px(box); + box.box_model().border.top = computed_values.border_top().width; + box.box_model().border.bottom = computed_values.border_bottom().width; + box.box_model().padding.top = computed_values.padding().top.resolved_or_zero(box, containing_block.width()).to_px(box); + box.box_model().padding.bottom = computed_values.padding().bottom.resolved_or_zero(box, containing_block.width()).to_px(box); + + if (!specified_height.is_auto()) { + float used_height = specified_height.to_px(box); + if (!specified_max_height.is_auto()) + used_height = min(used_height, specified_max_height.to_px(box)); + box.set_height(used_height); + } +} + +void FormattingContext::layout_absolutely_positioned_element(Box& box) +{ + auto& containing_block = context_box(); + auto& box_model = box.box_model(); + + auto specified_width = box.computed_values().width().resolved_or_auto(box, containing_block.width()); + + compute_width_for_absolutely_positioned_element(box); + layout_inside(box, LayoutMode::Default); + compute_height_for_absolutely_positioned_element(box); + + box_model.margin.left = box.computed_values().margin().left.resolved_or_auto(box, containing_block.width()).to_px(box); + box_model.margin.top = box.computed_values().margin().top.resolved_or_auto(box, containing_block.height()).to_px(box); + box_model.margin.right = box.computed_values().margin().right.resolved_or_auto(box, containing_block.width()).to_px(box); + box_model.margin.bottom = box.computed_values().margin().bottom.resolved_or_auto(box, containing_block.height()).to_px(box); + + box_model.border.left = box.computed_values().border_left().width; + box_model.border.right = box.computed_values().border_right().width; + box_model.border.top = box.computed_values().border_top().width; + box_model.border.bottom = box.computed_values().border_bottom().width; + + box_model.offset.left = box.computed_values().offset().left.resolved_or_auto(box, containing_block.width()).to_px(box); + box_model.offset.top = box.computed_values().offset().top.resolved_or_auto(box, containing_block.height()).to_px(box); + box_model.offset.right = box.computed_values().offset().right.resolved_or_auto(box, containing_block.width()).to_px(box); + box_model.offset.bottom = box.computed_values().offset().bottom.resolved_or_auto(box, containing_block.height()).to_px(box); + + if (box.computed_values().offset().left.is_auto() && specified_width.is_auto() && box.computed_values().offset().right.is_auto()) { + if (box.computed_values().margin().left.is_auto()) + box_model.margin.left = 0; + if (box.computed_values().margin().right.is_auto()) + box_model.margin.right = 0; + } + + Gfx::FloatPoint used_offset; + + if (!box.computed_values().offset().left.is_auto()) { + float x_offset = box_model.offset.left + + box_model.border_box().left; + used_offset.set_x(x_offset + box_model.margin.left); + } else if (!box.computed_values().offset().right.is_auto()) { + float x_offset = 0 + - box_model.offset.right + - box_model.border_box().right; + used_offset.set_x(containing_block.width() + x_offset - box.width() - box_model.margin.right); + } else { + float x_offset = box_model.margin_box().left; + used_offset.set_x(x_offset); + } + + if (!box.computed_values().offset().top.is_auto()) { + float y_offset = box_model.offset.top + + box_model.border_box().top; + used_offset.set_y(y_offset + box_model.margin.top); + } else if (!box.computed_values().offset().bottom.is_auto()) { + float y_offset = 0 + - box_model.offset.bottom + - box_model.border_box().bottom; + used_offset.set_y(containing_block.height() + y_offset - box.height() - box_model.margin.bottom); + } else { + float y_offset = box_model.margin_box().top; + used_offset.set_y(y_offset); + } + + box.set_offset(used_offset); +} + +void FormattingContext::compute_height_for_absolutely_positioned_replaced_element(ReplacedBox& box) +{ + // FIXME: Implement this. + return compute_height_for_absolutely_positioned_non_replaced_element(box); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.h b/Userland/Libraries/LibWeb/Layout/FormattingContext.h new file mode 100644 index 0000000000..aad024a04e --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.h @@ -0,0 +1,78 @@ +/* + * 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/Forward.h> + +namespace Web::Layout { + +class FormattingContext { +public: + virtual void run(Box&, LayoutMode) = 0; + + Box& context_box() { return *m_context_box; } + const Box& context_box() const { return *m_context_box; } + + FormattingContext* parent() { return m_parent; } + const FormattingContext* parent() const { return m_parent; } + + virtual bool is_block_formatting_context() const { return false; } + + static bool creates_block_formatting_context(const Box&); + + static float compute_width_for_replaced_element(const ReplacedBox&); + static float compute_height_for_replaced_element(const ReplacedBox&); + +protected: + FormattingContext(Box&, FormattingContext* parent = nullptr); + virtual ~FormattingContext(); + + void layout_inside(Box&, LayoutMode); + + struct ShrinkToFitResult { + float preferred_width { 0 }; + float preferred_minimum_width { 0 }; + }; + + static float tentative_width_for_replaced_element(const ReplacedBox&, const CSS::Length& width); + static float tentative_height_for_replaced_element(const ReplacedBox&, const CSS::Length& width); + + ShrinkToFitResult calculate_shrink_to_fit_widths(Box&); + + void layout_absolutely_positioned_element(Box&); + void compute_width_for_absolutely_positioned_element(Box&); + void compute_width_for_absolutely_positioned_non_replaced_element(Box&); + void compute_width_for_absolutely_positioned_replaced_element(ReplacedBox&); + void compute_height_for_absolutely_positioned_element(Box&); + void compute_height_for_absolutely_positioned_non_replaced_element(Box&); + void compute_height_for_absolutely_positioned_replaced_element(ReplacedBox&); + + FormattingContext* m_parent { nullptr }; + Box* m_context_box { nullptr }; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/FrameBox.cpp b/Userland/Libraries/LibWeb/Layout/FrameBox.cpp new file mode 100644 index 0000000000..79ad0e1815 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/FrameBox.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018-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 <LibGUI/Painter.h> +#include <LibGUI/ScrollBar.h> +#include <LibGUI/Widget.h> +#include <LibGfx/Font.h> +#include <LibGfx/StylePainter.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/InProcessWebView.h> +#include <LibWeb/Layout/FrameBox.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> +#include <LibWeb/Page/Frame.h> + +//#define DEBUG_HIGHLIGHT_FOCUSED_FRAME + +namespace Web::Layout { + +FrameBox::FrameBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style) + : ReplacedBox(document, element, move(style)) +{ +} + +FrameBox::~FrameBox() +{ +} + +void FrameBox::prepare_for_replaced_layout() +{ + ASSERT(dom_node().content_frame()); + + set_has_intrinsic_width(true); + set_has_intrinsic_height(true); + // FIXME: Do proper error checking, etc. + set_intrinsic_width(dom_node().attribute(HTML::AttributeNames::width).to_int().value_or(300)); + set_intrinsic_height(dom_node().attribute(HTML::AttributeNames::height).to_int().value_or(150)); +} + +void FrameBox::paint(PaintContext& context, PaintPhase phase) +{ + ReplacedBox::paint(context, phase); + + if (phase == PaintPhase::Foreground) { + auto* hosted_document = dom_node().content_document(); + if (!hosted_document) + return; + auto* hosted_layout_tree = hosted_document->layout_node(); + if (!hosted_layout_tree) + return; + + context.painter().save(); + auto old_viewport_rect = context.viewport_rect(); + + context.painter().add_clip_rect(enclosing_int_rect(absolute_rect())); + context.painter().translate(absolute_x(), absolute_y()); + + context.set_viewport_rect({ {}, dom_node().content_frame()->size() }); + const_cast<Layout::InitialContainingBlockBox*>(hosted_layout_tree)->paint_all_phases(context); + + context.set_viewport_rect(old_viewport_rect); + context.painter().restore(); + +#ifdef DEBUG_HIGHLIGHT_FOCUSED_FRAME + if (dom_node().content_frame()->is_focused_frame()) { + context.painter().draw_rect(absolute_rect().to<int>(), Color::Cyan); + } +#endif + } +} + +void FrameBox::did_set_rect() +{ + ReplacedBox::did_set_rect(); + + ASSERT(dom_node().content_frame()); + dom_node().content_frame()->set_size(size().to_type<int>()); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/FrameBox.h b/Userland/Libraries/LibWeb/Layout/FrameBox.h new file mode 100644 index 0000000000..93a5216f3f --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/FrameBox.h @@ -0,0 +1,49 @@ +/* + * 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/HTML/HTMLIFrameElement.h> +#include <LibWeb/Layout/ReplacedBox.h> + +namespace Web::Layout { + +class FrameBox final : public ReplacedBox { +public: + FrameBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>); + virtual ~FrameBox() override; + + virtual void paint(PaintContext&, PaintPhase) override; + virtual void prepare_for_replaced_layout() override; + + const HTML::HTMLIFrameElement& dom_node() const { return downcast<HTML::HTMLIFrameElement>(ReplacedBox::dom_node()); } + HTML::HTMLIFrameElement& dom_node() { return downcast<HTML::HTMLIFrameElement>(ReplacedBox::dom_node()); } + +private: + virtual void did_set_rect() override; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/ImageBox.cpp b/Userland/Libraries/LibWeb/Layout/ImageBox.cpp new file mode 100644 index 0000000000..a00c551c7a --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/ImageBox.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2018-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 <LibGUI/Painter.h> +#include <LibGfx/Font.h> +#include <LibGfx/FontDatabase.h> +#include <LibGfx/ImageDecoder.h> +#include <LibGfx/StylePainter.h> +#include <LibWeb/Layout/ImageBox.h> + +namespace Web::Layout { + +ImageBox::ImageBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style, const ImageLoader& image_loader) + : ReplacedBox(document, element, move(style)) + , m_image_loader(image_loader) +{ +} + +ImageBox::~ImageBox() +{ +} + +int ImageBox::preferred_width() const +{ + return dom_node().attribute(HTML::AttributeNames::width).to_int().value_or(m_image_loader.width()); +} + +int ImageBox::preferred_height() const +{ + return dom_node().attribute(HTML::AttributeNames::height).to_int().value_or(m_image_loader.height()); +} + +void ImageBox::prepare_for_replaced_layout() +{ + if (!m_image_loader.has_loaded_or_failed()) { + set_has_intrinsic_width(true); + set_has_intrinsic_height(true); + set_intrinsic_width(0); + set_intrinsic_height(0); + } else { + if (m_image_loader.width()) { + set_has_intrinsic_width(true); + set_intrinsic_width(m_image_loader.width()); + } + if (m_image_loader.height()) { + set_has_intrinsic_height(true); + set_intrinsic_height(m_image_loader.height()); + } + + if (m_image_loader.width() && m_image_loader.height()) { + set_has_intrinsic_ratio(true); + set_intrinsic_ratio((float)m_image_loader.width() / (float)m_image_loader.height()); + } else { + set_has_intrinsic_ratio(false); + } + } + + if (renders_as_alt_text()) { + auto& image_element = downcast<HTML::HTMLImageElement>(dom_node()); + auto& font = Gfx::FontDatabase::default_font(); + auto alt = image_element.alt(); + if (alt.is_empty()) + alt = image_element.src(); + set_width(font.width(alt) + 16); + set_height(font.glyph_height() + 16); + } + + if (!has_intrinsic_width() && !has_intrinsic_height()) { + set_width(16); + set_height(16); + } +} + +void ImageBox::paint(PaintContext& context, PaintPhase phase) +{ + if (!is_visible()) + return; + + // FIXME: This should be done at a different level. Also rect() does not include padding etc! + if (!context.viewport_rect().intersects(enclosing_int_rect(absolute_rect()))) + return; + + ReplacedBox::paint(context, phase); + + if (phase == PaintPhase::Foreground) { + if (renders_as_alt_text()) { + auto& image_element = downcast<HTML::HTMLImageElement>(dom_node()); + context.painter().set_font(Gfx::FontDatabase::default_font()); + Gfx::StylePainter::paint_frame(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), Gfx::FrameShape::Container, Gfx::FrameShadow::Sunken, 2); + auto alt = image_element.alt(); + if (alt.is_empty()) + alt = image_element.src(); + context.painter().draw_text(enclosing_int_rect(absolute_rect()), alt, Gfx::TextAlignment::Center, computed_values().color(), Gfx::TextElision::Right); + } else if (auto bitmap = m_image_loader.bitmap()) { + context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *bitmap, bitmap->rect()); + } + } +} + +bool ImageBox::renders_as_alt_text() const +{ + if (is<HTML::HTMLImageElement>(dom_node())) + return !m_image_loader.has_image(); + return false; +} + +void ImageBox::set_visible_in_viewport(Badge<Layout::InitialContainingBlockBox>, bool visible_in_viewport) +{ + m_image_loader.set_visible_in_viewport(visible_in_viewport); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/ImageBox.h b/Userland/Libraries/LibWeb/Layout/ImageBox.h new file mode 100644 index 0000000000..a404ca1ca0 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/ImageBox.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018-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/HTML/HTMLImageElement.h> +#include <LibWeb/Layout/ReplacedBox.h> + +namespace Web::Layout { + +class ImageBox : public ReplacedBox { +public: + ImageBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>, const ImageLoader&); + virtual ~ImageBox() override; + + virtual void prepare_for_replaced_layout() override; + virtual void paint(PaintContext&, PaintPhase) override; + + const DOM::Element& dom_node() const { return static_cast<const DOM::Element&>(ReplacedBox::dom_node()); } + + bool renders_as_alt_text() const; + + void set_visible_in_viewport(Badge<InitialContainingBlockBox>, bool); + +private: + int preferred_width() const; + int preferred_height() const; + + const ImageLoader& m_image_loader; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.cpp b/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.cpp new file mode 100644 index 0000000000..320f0f770c --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2018-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 <LibWeb/Dump.h> +#include <LibWeb/Layout/ImageBox.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> +#include <LibWeb/Layout/WidgetBox.h> +#include <LibWeb/Page/Frame.h> +#include <LibWeb/Painting/StackingContext.h> + +namespace Web::Layout { + +InitialContainingBlockBox::InitialContainingBlockBox(DOM::Document& document, NonnullRefPtr<CSS::StyleProperties> style) + : BlockBox(document, &document, move(style)) +{ +} + +InitialContainingBlockBox::~InitialContainingBlockBox() +{ +} + +void InitialContainingBlockBox::build_stacking_context_tree() +{ + if (stacking_context()) + return; + + set_stacking_context(make<StackingContext>(*this, nullptr)); + + for_each_in_subtree_of_type<Box>([&](Box& box) { + if (&box == this) + return IterationDecision::Continue; + if (!box.establishes_stacking_context()) { + ASSERT(!box.stacking_context()); + return IterationDecision::Continue; + } + auto* parent_context = box.enclosing_stacking_context(); + ASSERT(parent_context); + box.set_stacking_context(make<StackingContext>(box, parent_context)); + return IterationDecision::Continue; + }); +} + +void InitialContainingBlockBox::did_set_viewport_rect(Badge<Frame>, const Gfx::IntRect& a_viewport_rect) +{ + Gfx::FloatRect viewport_rect(a_viewport_rect.x(), a_viewport_rect.y(), a_viewport_rect.width(), a_viewport_rect.height()); + for_each_in_subtree_of_type<ImageBox>([&](auto& layout_image) { + const_cast<ImageBox&>(layout_image).set_visible_in_viewport({}, viewport_rect.intersects(layout_image.absolute_rect())); + return IterationDecision::Continue; + }); +} + +void InitialContainingBlockBox::paint_all_phases(PaintContext& context) +{ + paint(context, PaintPhase::Background); + paint(context, PaintPhase::Border); + paint(context, PaintPhase::Foreground); + if (context.has_focus()) + paint(context, PaintPhase::FocusOutline); + paint(context, PaintPhase::Overlay); +} + +void InitialContainingBlockBox::paint(PaintContext& context, PaintPhase phase) +{ + stacking_context()->paint(context, phase); +} + +HitTestResult InitialContainingBlockBox::hit_test(const Gfx::IntPoint& position, HitTestType type) const +{ + return stacking_context()->hit_test(position, type); +} + +void InitialContainingBlockBox::recompute_selection_states() +{ + SelectionState state = SelectionState::None; + + auto selection = this->selection().normalized(); + + for_each_in_subtree([&](auto& layout_node) { + if (!selection.is_valid()) { + // Everything gets SelectionState::None. + } else if (&layout_node == selection.start().layout_node && &layout_node == selection.end().layout_node) { + state = SelectionState::StartAndEnd; + } else if (&layout_node == selection.start().layout_node) { + state = SelectionState::Start; + } else if (&layout_node == selection.end().layout_node) { + state = SelectionState::End; + } else { + if (state == SelectionState::Start) + state = SelectionState::Full; + else if (state == SelectionState::End || state == SelectionState::StartAndEnd) + state = SelectionState::None; + } + layout_node.set_selection_state(state); + return IterationDecision::Continue; + }); +} + +void InitialContainingBlockBox::set_selection(const LayoutRange& selection) +{ + m_selection = selection; + recompute_selection_states(); +} + +void InitialContainingBlockBox::set_selection_end(const LayoutPosition& position) +{ + m_selection.set_end(position); + recompute_selection_states(); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.h b/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.h new file mode 100644 index 0000000000..b7b4810364 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-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/DOM/Document.h> +#include <LibWeb/Layout/BlockBox.h> + +namespace Web::Layout { + +class InitialContainingBlockBox final : public BlockBox { +public: + explicit InitialContainingBlockBox(DOM::Document&, NonnullRefPtr<CSS::StyleProperties>); + virtual ~InitialContainingBlockBox() override; + + const DOM::Document& dom_node() const { return static_cast<const DOM::Document&>(*Node::dom_node()); } + + void paint_all_phases(PaintContext&); + virtual void paint(PaintContext&, PaintPhase) override; + + virtual HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const override; + + const LayoutRange& selection() const { return m_selection; } + void set_selection(const LayoutRange&); + void set_selection_end(const LayoutPosition&); + + void did_set_viewport_rect(Badge<Frame>, const Gfx::IntRect&); + + void build_stacking_context_tree(); + + void recompute_selection_states(); + +private: + LayoutRange m_selection; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp new file mode 100644 index 0000000000..b309e02e04 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp @@ -0,0 +1,254 @@ +/* + * 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 <LibWeb/CSS/Length.h> +#include <LibWeb/DOM/Node.h> +#include <LibWeb/Dump.h> +#include <LibWeb/Layout/BlockBox.h> +#include <LibWeb/Layout/BlockFormattingContext.h> +#include <LibWeb/Layout/Box.h> +#include <LibWeb/Layout/InlineFormattingContext.h> +#include <LibWeb/Layout/InlineNode.h> +#include <LibWeb/Layout/ReplacedBox.h> + +namespace Web::Layout { + +InlineFormattingContext::InlineFormattingContext(Box& containing_block, FormattingContext* parent) + : FormattingContext(containing_block, parent) +{ +} + +InlineFormattingContext::~InlineFormattingContext() +{ +} + +struct AvailableSpaceForLineInfo { + float left { 0 }; + float right { 0 }; +}; + +static AvailableSpaceForLineInfo available_space_for_line(const InlineFormattingContext& context, size_t line_index) +{ + AvailableSpaceForLineInfo info; + + // FIXME: This is a total hack guess since we don't actually know the final y position of lines here! + float line_height = context.containing_block().line_height(); + float y = (line_index * line_height); + + auto& bfc = static_cast<const BlockFormattingContext&>(*context.parent()); + + for (ssize_t i = bfc.left_floating_boxes().size() - 1; i >= 0; --i) { + auto& floating_box = *bfc.left_floating_boxes().at(i); + auto rect = floating_box.margin_box_as_relative_rect(); + if (rect.contains_vertically(y)) { + info.left = rect.right() + 1; + break; + } + } + + info.right = context.containing_block().width(); + + for (ssize_t i = bfc.right_floating_boxes().size() - 1; i >= 0; --i) { + auto& floating_box = *bfc.right_floating_boxes().at(i); + auto rect = floating_box.margin_box_as_relative_rect(); + if (rect.contains_vertically(y)) { + info.right = rect.left() - 1; + break; + } + } + + return info; +} + +float InlineFormattingContext::available_width_at_line(size_t line_index) const +{ + auto info = available_space_for_line(*this, line_index); + return info.right - info.left; +} + +void InlineFormattingContext::run(Box&, LayoutMode layout_mode) +{ + ASSERT(containing_block().children_are_inline()); + containing_block().line_boxes().clear(); + containing_block().for_each_child([&](auto& child) { + ASSERT(child.is_inline()); + if (is<Box>(child) && child.is_absolutely_positioned()) { + layout_absolutely_positioned_element(downcast<Box>(child)); + return; + } + + child.split_into_lines(*this, layout_mode); + }); + + for (auto& line_box : containing_block().line_boxes()) { + line_box.trim_trailing_whitespace(); + } + + // If there's an empty line box at the bottom, just remove it instead of giving it height. + if (!containing_block().line_boxes().is_empty() && containing_block().line_boxes().last().fragments().is_empty()) + containing_block().line_boxes().take_last(); + + auto text_align = containing_block().computed_values().text_align(); + float min_line_height = containing_block().line_height(); + float content_height = 0; + float max_linebox_width = 0; + + for (size_t line_index = 0; line_index < containing_block().line_boxes().size(); ++line_index) { + auto& line_box = containing_block().line_boxes()[line_index]; + float max_height = min_line_height; + for (auto& fragment : line_box.fragments()) { + max_height = max(max_height, fragment.height()); + } + + float x_offset = available_space_for_line(*this, line_index).left; + + float excess_horizontal_space = (float)containing_block().width() - line_box.width(); + + switch (text_align) { + case CSS::TextAlign::Center: + case CSS::TextAlign::LibwebCenter: + x_offset += excess_horizontal_space / 2; + break; + case CSS::TextAlign::Right: + x_offset += excess_horizontal_space; + break; + case CSS::TextAlign::Left: + case CSS::TextAlign::Justify: + default: + break; + } + + float excess_horizontal_space_including_whitespace = excess_horizontal_space; + int whitespace_count = 0; + if (text_align == CSS::TextAlign::Justify) { + for (auto& fragment : line_box.fragments()) { + if (fragment.is_justifiable_whitespace()) { + ++whitespace_count; + excess_horizontal_space_including_whitespace += fragment.width(); + } + } + } + + float justified_space_width = whitespace_count ? (excess_horizontal_space_including_whitespace / (float)whitespace_count) : 0; + + for (size_t i = 0; i < line_box.fragments().size(); ++i) { + auto& fragment = line_box.fragments()[i]; + + if (fragment.type() == LineBoxFragment::Type::Leading || fragment.type() == LineBoxFragment::Type::Trailing) { + fragment.set_height(max_height); + } else { + fragment.set_height(max(min_line_height, fragment.height())); + } + + // Vertically align everyone's bottom to the line. + // FIXME: Support other kinds of vertical alignment. + fragment.set_offset({ roundf(x_offset + fragment.offset().x()), content_height + (max_height - fragment.height()) }); + + if (text_align == CSS::TextAlign::Justify + && fragment.is_justifiable_whitespace() + && fragment.width() != justified_space_width) { + float diff = justified_space_width - fragment.width(); + fragment.set_width(justified_space_width); + // Shift subsequent sibling fragments to the right to adjust for change in width. + for (size_t j = i + 1; j < line_box.fragments().size(); ++j) { + auto offset = line_box.fragments()[j].offset(); + offset.move_by(diff, 0); + line_box.fragments()[j].set_offset(offset); + } + } + } + + if (!line_box.fragments().is_empty()) { + float left_edge = line_box.fragments().first().offset().x(); + float right_edge = line_box.fragments().last().offset().x() + line_box.fragments().last().width(); + float final_line_box_width = right_edge - left_edge; + line_box.m_width = final_line_box_width; + max_linebox_width = max(max_linebox_width, final_line_box_width); + } + + content_height += max_height; + } + + if (layout_mode != LayoutMode::Default) { + containing_block().set_width(max_linebox_width); + } + + containing_block().set_height(content_height); +} + +void InlineFormattingContext::dimension_box_on_line(Box& box, LayoutMode layout_mode) +{ + if (is<ReplacedBox>(box)) { + auto& replaced = downcast<ReplacedBox>(box); + replaced.set_width(compute_width_for_replaced_element(replaced)); + replaced.set_height(compute_height_for_replaced_element(replaced)); + return; + } + + if (box.is_inline_block()) { + auto& inline_block = const_cast<BlockBox&>(downcast<BlockBox>(box)); + + if (inline_block.computed_values().width().is_undefined_or_auto()) { + auto result = calculate_shrink_to_fit_widths(inline_block); + + auto margin_left = inline_block.computed_values().margin().left.resolved_or_zero(inline_block, containing_block().width()).to_px(inline_block); + auto border_left_width = inline_block.computed_values().border_left().width; + auto padding_left = inline_block.computed_values().padding().left.resolved_or_zero(inline_block, containing_block().width()).to_px(inline_block); + + auto margin_right = inline_block.computed_values().margin().right.resolved_or_zero(inline_block, containing_block().width()).to_px(inline_block); + auto border_right_width = inline_block.computed_values().border_right().width; + auto padding_right = inline_block.computed_values().padding().right.resolved_or_zero(inline_block, containing_block().width()).to_px(inline_block); + + auto available_width = containing_block().width() + - margin_left + - border_left_width + - padding_left + - padding_right + - border_right_width + - margin_right; + + auto width = min(max(result.preferred_minimum_width, available_width), result.preferred_width); + inline_block.set_width(width); + } else { + inline_block.set_width(inline_block.computed_values().width().resolved_or_zero(inline_block, containing_block().width()).to_px(inline_block)); + } + layout_inside(inline_block, layout_mode); + + if (inline_block.computed_values().height().is_undefined_or_auto()) { + // FIXME: (10.6.6) If 'height' is 'auto', the height depends on the element's descendants per 10.6.7. + } else { + inline_block.set_height(inline_block.computed_values().height().resolved_or_zero(inline_block, containing_block().height()).to_px(inline_block)); + } + return; + } + + // Non-replaced, non-inline-block, box on a line!? + // I don't think we should be here. Dump the box tree so we can take a look at it. + dbgln("FIXME: I've been asked to dimension a non-replaced, non-inline-block box on a line:"); + dump_tree(box); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h new file mode 100644 index 0000000000..38af898d21 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h @@ -0,0 +1,50 @@ +/* + * 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 <AK/Types.h> +#include <LibWeb/Forward.h> +#include <LibWeb/Layout/FormattingContext.h> + +namespace Web::Layout { + +class InlineFormattingContext final : public FormattingContext { +public: + InlineFormattingContext(Box& containing_block, FormattingContext* parent); + ~InlineFormattingContext(); + + Box& containing_block() { return context_box(); } + const Box& containing_block() const { return context_box(); } + + virtual void run(Box&, LayoutMode) override; + + float available_width_at_line(size_t line_index) const; + + void dimension_box_on_line(Box&, LayoutMode); +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/InlineNode.cpp b/Userland/Libraries/LibWeb/Layout/InlineNode.cpp new file mode 100644 index 0000000000..ab3974d54f --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/InlineNode.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018-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 <LibGfx/Painter.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/Layout/BlockBox.h> +#include <LibWeb/Layout/InlineFormattingContext.h> +#include <LibWeb/Layout/InlineNode.h> + +namespace Web::Layout { + +InlineNode::InlineNode(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style) + : Layout::NodeWithStyleAndBoxModelMetrics(document, &element, move(style)) +{ + set_inline(true); +} + +InlineNode::~InlineNode() +{ +} + +void InlineNode::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode) +{ + auto& containing_block = context.context_box(); + + if (!computed_values().padding().left.is_undefined_or_auto()) { + float padding_left = computed_values().padding().left.resolved(CSS::Length::make_px(0), *this, containing_block.width()).to_px(*this); + containing_block.ensure_last_line_box().add_fragment(*this, 0, 0, padding_left, 0, LineBoxFragment::Type::Leading); + } + + NodeWithStyleAndBoxModelMetrics::split_into_lines(context, layout_mode); + + if (!computed_values().padding().right.is_undefined_or_auto()) { + float padding_right = computed_values().padding().right.resolved(CSS::Length::make_px(0), *this, containing_block.width()).to_px(*this); + containing_block.ensure_last_line_box().add_fragment(*this, 0, 0, padding_right, 0, LineBoxFragment::Type::Trailing); + } +} + +void InlineNode::paint_fragment(PaintContext& context, const LineBoxFragment& fragment, PaintPhase phase) const +{ + auto& painter = context.painter(); + + if (phase == PaintPhase::Background) { + painter.fill_rect(enclosing_int_rect(fragment.absolute_rect()), computed_values().background_color()); + } +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/InlineNode.h b/Userland/Libraries/LibWeb/Layout/InlineNode.h new file mode 100644 index 0000000000..8f47b24f34 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/InlineNode.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-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/Layout/Box.h> + +namespace Web::Layout { + +class InlineNode : public NodeWithStyleAndBoxModelMetrics { +public: + InlineNode(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>); + virtual ~InlineNode() override; + + virtual void paint_fragment(PaintContext&, const LineBoxFragment&, PaintPhase) const override; + + virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/LayoutPosition.cpp b/Userland/Libraries/LibWeb/Layout/LayoutPosition.cpp new file mode 100644 index 0000000000..307c81f9f7 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/LayoutPosition.cpp @@ -0,0 +1,67 @@ +/* + * 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 <LibWeb/DOM/Position.h> +#include <LibWeb/DOM/Range.h> +#include <LibWeb/Layout/LayoutPosition.h> +#include <LibWeb/Layout/Node.h> + +namespace Web::Layout { + +DOM::Position LayoutPosition::to_dom_position() const +{ + if (!layout_node) + return {}; + + // FIXME: Verify that there are no shenanigans going on. + return { const_cast<DOM::Node&>(*layout_node->dom_node()), (unsigned)index_in_node }; +} + +LayoutRange LayoutRange::normalized() const +{ + if (!is_valid()) + return {}; + if (m_start.layout_node == m_end.layout_node) { + if (m_start.index_in_node < m_end.index_in_node) + return *this; + return { m_end, m_start }; + } + if (m_start.layout_node->is_before(*m_end.layout_node)) + return *this; + return { m_end, m_start }; +} + +NonnullRefPtr<DOM::Range> LayoutRange::to_dom_range() const +{ + ASSERT(is_valid()); + + auto start = m_start.to_dom_position(); + auto end = m_end.to_dom_position(); + + return DOM::Range::create(*start.node(), start.offset(), *end.node(), end.offset()); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/LayoutPosition.h b/Userland/Libraries/LibWeb/Layout/LayoutPosition.h new file mode 100644 index 0000000000..840a9c388b --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/LayoutPosition.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018-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 <AK/RefPtr.h> +#include <LibWeb/Forward.h> +#include <LibWeb/Layout/Node.h> + +namespace Web::Layout { + +class Node; + +struct LayoutPosition { + RefPtr<Node> layout_node; + int index_in_node { 0 }; + + DOM::Position to_dom_position() const; +}; + +class LayoutRange { +public: + LayoutRange() { } + LayoutRange(const LayoutPosition& start, const LayoutPosition& end) + : m_start(start) + , m_end(end) + { + } + + bool is_valid() const { return m_start.layout_node && m_end.layout_node; } + + void set(const LayoutPosition& start, const LayoutPosition& end) + { + m_start = start; + m_end = end; + } + + void set_start(const LayoutPosition& start) { m_start = start; } + void set_end(const LayoutPosition& end) { m_end = end; } + + const LayoutPosition& start() const { return m_start; } + LayoutPosition& start() { return m_start; } + const LayoutPosition& end() const { return m_end; } + LayoutPosition& end() { return m_end; } + + LayoutRange normalized() const; + + NonnullRefPtr<DOM::Range> to_dom_range() const; + +private: + LayoutPosition m_start; + LayoutPosition m_end; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/LineBox.cpp b/Userland/Libraries/LibWeb/Layout/LineBox.cpp new file mode 100644 index 0000000000..df08bfe9eb --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/LineBox.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018-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/Utf8View.h> +#include <LibWeb/Layout/Box.h> +#include <LibWeb/Layout/LineBox.h> +#include <LibWeb/Layout/Node.h> +#include <LibWeb/Layout/TextNode.h> +#include <ctype.h> + +namespace Web::Layout { + +void LineBox::add_fragment(Node& layout_node, int start, int length, float width, float height, LineBoxFragment::Type fragment_type) +{ + bool text_align_is_justify = layout_node.computed_values().text_align() == CSS::TextAlign::Justify; + if (!text_align_is_justify && !m_fragments.is_empty() && &m_fragments.last().layout_node() == &layout_node) { + // The fragment we're adding is from the last Layout::Node on the line. + // Expand the last fragment instead of adding a new one with the same Layout::Node. + m_fragments.last().m_length = (start - m_fragments.last().m_start) + length; + m_fragments.last().set_width(m_fragments.last().width() + width); + } else { + m_fragments.append(make<LineBoxFragment>(layout_node, start, length, Gfx::FloatPoint(m_width, 0.0f), Gfx::FloatSize(width, height), fragment_type)); + } + m_width += width; + + if (is<Box>(layout_node)) + downcast<Box>(layout_node).set_containing_line_box_fragment(m_fragments.last()); +} + +void LineBox::trim_trailing_whitespace() +{ + while (!m_fragments.is_empty() && m_fragments.last().is_justifiable_whitespace()) { + auto fragment = m_fragments.take_last(); + m_width -= fragment->width(); + } + + if (m_fragments.is_empty()) + return; + + auto last_text = m_fragments.last().text(); + if (last_text.is_null()) + return; + auto& last_fragment = m_fragments.last(); + + int space_width = last_fragment.layout_node().font().glyph_width(' '); + while (last_fragment.length() && isspace(last_text[last_fragment.length() - 1])) { + last_fragment.m_length -= 1; + last_fragment.set_width(last_fragment.width() - space_width); + m_width -= space_width; + } +} + +bool LineBox::is_empty_or_ends_in_whitespace() const +{ + if (m_fragments.is_empty()) + return true; + return m_fragments.last().ends_in_whitespace(); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/LineBox.h b/Userland/Libraries/LibWeb/Layout/LineBox.h new file mode 100644 index 0000000000..dcf1efd081 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/LineBox.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018-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 <AK/NonnullOwnPtrVector.h> +#include <AK/Vector.h> +#include <LibWeb/Layout/LineBoxFragment.h> + +namespace Web::Layout { + +class LineBox { +public: + LineBox() { } + + float width() const { return m_width; } + + void add_fragment(Node& layout_node, int start, int length, float width, float height, LineBoxFragment::Type = LineBoxFragment::Type::Normal); + + const NonnullOwnPtrVector<LineBoxFragment>& fragments() const { return m_fragments; } + NonnullOwnPtrVector<LineBoxFragment>& fragments() { return m_fragments; } + + void trim_trailing_whitespace(); + + bool is_empty_or_ends_in_whitespace() const; + +private: + friend class BlockBox; + friend class InlineFormattingContext; + NonnullOwnPtrVector<LineBoxFragment> m_fragments; + float m_width { 0 }; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/LineBoxFragment.cpp b/Userland/Libraries/LibWeb/Layout/LineBoxFragment.cpp new file mode 100644 index 0000000000..ba49f93d18 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/LineBoxFragment.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2018-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/Utf8View.h> +#include <LibGUI/Painter.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> +#include <LibWeb/Layout/LineBoxFragment.h> +#include <LibWeb/Layout/TextNode.h> +#include <LibWeb/Painting/BorderPainting.h> +#include <LibWeb/Painting/PaintContext.h> +#include <ctype.h> + +namespace Web::Layout { + +void LineBoxFragment::paint(PaintContext& context, PaintPhase phase) +{ + for (auto* ancestor = layout_node().parent(); ancestor; ancestor = ancestor->parent()) { + if (!ancestor->is_visible()) + return; + } + + layout_node().paint_fragment(context, *this, phase); +} + +bool LineBoxFragment::ends_in_whitespace() const +{ + auto text = this->text(); + if (text.is_empty()) + return false; + return isspace(text[text.length() - 1]); +} + +bool LineBoxFragment::is_justifiable_whitespace() const +{ + return text() == " "; +} + +StringView LineBoxFragment::text() const +{ + if (!is<TextNode>(layout_node())) + return {}; + return downcast<TextNode>(layout_node()).text_for_rendering().substring_view(m_start, m_length); +} + +const Gfx::FloatRect LineBoxFragment::absolute_rect() const +{ + Gfx::FloatRect rect { {}, size() }; + rect.set_location(m_layout_node.containing_block()->absolute_position()); + rect.move_by(offset()); + return rect; +} + +int LineBoxFragment::text_index_at(float x) const +{ + if (!is<TextNode>(layout_node())) + return 0; + auto& layout_text = downcast<TextNode>(layout_node()); + auto& font = layout_text.font(); + Utf8View view(text()); + + float relative_x = x - absolute_x(); + float glyph_spacing = font.glyph_spacing(); + + if (relative_x < 0) + return 0; + + float width_so_far = 0; + for (auto it = view.begin(); it != view.end(); ++it) { + float glyph_width = font.glyph_or_emoji_width(*it); + if ((width_so_far + (glyph_width + glyph_spacing) / 2) > relative_x) + return m_start + view.byte_offset_of(it); + width_so_far += glyph_width + glyph_spacing; + } + return m_start + m_length; +} + +Gfx::FloatRect LineBoxFragment::selection_rect(const Gfx::Font& font) const +{ + if (layout_node().selection_state() == Node::SelectionState::None) + return {}; + + if (layout_node().selection_state() == Node::SelectionState::Full) + return absolute_rect(); + + auto selection = layout_node().root().selection().normalized(); + if (!selection.is_valid()) + return {}; + if (!is<TextNode>(layout_node())) + return {}; + + const auto start_index = m_start; + const auto end_index = m_start + m_length; + auto text = this->text(); + + if (layout_node().selection_state() == Node::SelectionState::StartAndEnd) { + // we are in the start/end node (both the same) + if (start_index > selection.end().index_in_node) + return {}; + if (end_index < selection.start().index_in_node) + return {}; + + if (selection.start().index_in_node == selection.end().index_in_node) + return {}; + + auto selection_start_in_this_fragment = max(0, selection.start().index_in_node - m_start); + auto selection_end_in_this_fragment = min(m_length, selection.end().index_in_node - m_start); + auto pixel_distance_to_first_selected_character = font.width(text.substring_view(0, selection_start_in_this_fragment)); + auto pixel_width_of_selection = font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment)) + 1; + + auto rect = absolute_rect(); + rect.set_x(rect.x() + pixel_distance_to_first_selected_character); + rect.set_width(pixel_width_of_selection); + + return rect; + } + if (layout_node().selection_state() == Node::SelectionState::Start) { + // we are in the start node + if (end_index < selection.start().index_in_node) + return {}; + + auto selection_start_in_this_fragment = max(0, selection.start().index_in_node - m_start); + auto selection_end_in_this_fragment = m_length; + auto pixel_distance_to_first_selected_character = font.width(text.substring_view(0, selection_start_in_this_fragment)); + auto pixel_width_of_selection = font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment)) + 1; + + auto rect = absolute_rect(); + rect.set_x(rect.x() + pixel_distance_to_first_selected_character); + rect.set_width(pixel_width_of_selection); + + return rect; + } + if (layout_node().selection_state() == Node::SelectionState::End) { + // we are in the end node + if (start_index > selection.end().index_in_node) + return {}; + + auto selection_start_in_this_fragment = 0; + auto selection_end_in_this_fragment = min(selection.end().index_in_node, m_length); + auto pixel_distance_to_first_selected_character = font.width(text.substring_view(0, selection_start_in_this_fragment)); + auto pixel_width_of_selection = font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment)) + 1; + + auto rect = absolute_rect(); + rect.set_x(rect.x() + pixel_distance_to_first_selected_character); + rect.set_width(pixel_width_of_selection); + + return rect; + } + return {}; +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/LineBoxFragment.h b/Userland/Libraries/LibWeb/Layout/LineBoxFragment.h new file mode 100644 index 0000000000..03819f0fb3 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/LineBoxFragment.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2018-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 <AK/Weakable.h> +#include <LibGfx/Forward.h> +#include <LibGfx/Rect.h> +#include <LibWeb/Forward.h> + +namespace Web::Layout { + +class LineBoxFragment : public Weakable<LineBoxFragment> { + friend class LineBox; + +public: + enum class Type { + Normal, + Leading, + Trailing, + }; + + LineBoxFragment(Node& layout_node, int start, int length, const Gfx::FloatPoint& offset, const Gfx::FloatSize& size, Type type) + : m_layout_node(layout_node) + , m_start(start) + , m_length(length) + , m_offset(offset) + , m_size(size) + , m_type(type) + { + } + + Node& layout_node() const { return m_layout_node; } + int start() const { return m_start; } + int length() const { return m_length; } + const Gfx::FloatRect absolute_rect() const; + Type type() const { return m_type; } + + const Gfx::FloatPoint& offset() const { return m_offset; } + void set_offset(const Gfx::FloatPoint& offset) { m_offset = offset; } + + const Gfx::FloatSize& size() const { return m_size; } + void set_width(float width) { m_size.set_width(width); } + void set_height(float height) { m_size.set_height(height); } + float width() const { return m_size.width(); } + float height() const { return m_size.height(); } + + float absolute_x() const { return absolute_rect().x(); } + + void paint(PaintContext&, PaintPhase); + + bool ends_in_whitespace() const; + bool is_justifiable_whitespace() const; + StringView text() const; + + int text_index_at(float x) const; + + Gfx::FloatRect selection_rect(const Gfx::Font&) const; + +private: + Node& m_layout_node; + int m_start { 0 }; + int m_length { 0 }; + Gfx::FloatPoint m_offset; + Gfx::FloatSize m_size; + Type m_type { Type::Normal }; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/ListItemBox.cpp b/Userland/Libraries/LibWeb/Layout/ListItemBox.cpp new file mode 100644 index 0000000000..06ed92533e --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/ListItemBox.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018-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 <LibWeb/Layout/ListItemBox.h> +#include <LibWeb/Layout/ListItemMarkerBox.h> + +namespace Web::Layout { + +ListItemBox::ListItemBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style) + : Layout::BlockBox(document, &element, move(style)) +{ +} + +ListItemBox::~ListItemBox() +{ +} + +void ListItemBox::layout_marker() +{ + if (m_marker) { + remove_child(*m_marker); + m_marker = nullptr; + } + + if (computed_values().list_style_type() == CSS::ListStyleType::None) + return; + + if (!m_marker) { + m_marker = adopt(*new ListItemMarkerBox(document())); + if (first_child()) + m_marker->set_inline(first_child()->is_inline()); + append_child(*m_marker); + } + + m_marker->set_offset(-8, 0); + m_marker->set_size(4, height()); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/ListItemBox.h b/Userland/Libraries/LibWeb/Layout/ListItemBox.h new file mode 100644 index 0000000000..39b5af84e3 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/ListItemBox.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-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/DOM/Element.h> +#include <LibWeb/Layout/BlockBox.h> + +namespace Web::Layout { + +class ListItemMarkerBox; + +class ListItemBox final : public BlockBox { +public: + ListItemBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>); + virtual ~ListItemBox() override; + + void layout_marker(); + +private: + RefPtr<ListItemMarkerBox> m_marker; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp b/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp new file mode 100644 index 0000000000..2b387112e6 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-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 <LibGUI/Painter.h> +#include <LibWeb/Layout/ListItemMarkerBox.h> + +namespace Web::Layout { + +ListItemMarkerBox::ListItemMarkerBox(DOM::Document& document) + : Box(document, nullptr, CSS::StyleProperties::create()) +{ +} + +ListItemMarkerBox::~ListItemMarkerBox() +{ +} + +void ListItemMarkerBox::paint(PaintContext& context, PaintPhase phase) +{ + if (phase != PaintPhase::Foreground) + return; + Gfx::IntRect bullet_rect { 0, 0, 4, 4 }; + bullet_rect.center_within(enclosing_int_rect(absolute_rect())); + // FIXME: It would be nicer to not have to go via the parent here to get our inherited style. + auto color = parent()->computed_values().color(); + context.painter().fill_rect(bullet_rect, color); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.h b/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.h new file mode 100644 index 0000000000..e570fd21df --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-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/Layout/Box.h> + +namespace Web::Layout { + +class ListItemMarkerBox final : public Box { +public: + explicit ListItemMarkerBox(DOM::Document&); + virtual ~ListItemMarkerBox() override; + + virtual void paint(PaintContext&, PaintPhase) override; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp new file mode 100644 index 0000000000..7af494a0fb --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/Node.cpp @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2018-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/Demangle.h> +#include <LibGUI/Painter.h> +#include <LibGfx/FontDatabase.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/Dump.h> +#include <LibWeb/HTML/HTMLHtmlElement.h> +#include <LibWeb/Layout/BlockBox.h> +#include <LibWeb/Layout/FormattingContext.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> +#include <LibWeb/Layout/Node.h> +#include <LibWeb/Layout/ReplacedBox.h> +#include <LibWeb/Layout/TextNode.h> +#include <LibWeb/Page/Frame.h> +#include <typeinfo> + +namespace Web::Layout { + +Node::Node(DOM::Document& document, DOM::Node* node) + : m_document(document) + , m_dom_node(node) +{ + if (m_dom_node) + m_dom_node->set_layout_node({}, this); +} + +Node::~Node() +{ + if (m_dom_node && m_dom_node->layout_node() == this) + m_dom_node->set_layout_node({}, nullptr); +} + +bool Node::can_contain_boxes_with_position_absolute() const +{ + return computed_values().position() != CSS::Position::Static || is<InitialContainingBlockBox>(*this); +} + +const BlockBox* Node::containing_block() const +{ + auto nearest_block_ancestor = [this] { + auto* ancestor = parent(); + while (ancestor && !is<BlockBox>(*ancestor)) + ancestor = ancestor->parent(); + return downcast<BlockBox>(ancestor); + }; + + if (is<TextNode>(*this)) + return nearest_block_ancestor(); + + auto position = computed_values().position(); + + if (position == CSS::Position::Absolute) { + auto* ancestor = parent(); + while (ancestor && !ancestor->can_contain_boxes_with_position_absolute()) + ancestor = ancestor->parent(); + while (ancestor && (!is<BlockBox>(ancestor) || ancestor->is_anonymous())) + ancestor = ancestor->containing_block(); + return downcast<BlockBox>(ancestor); + } + + if (position == CSS::Position::Fixed) + return &root(); + + return nearest_block_ancestor(); +} + +void Node::paint(PaintContext& context, PaintPhase phase) +{ + if (!is_visible()) + return; + + before_children_paint(context, phase); + + for_each_child_in_paint_order([&](auto& child) { + child.paint(context, phase); + }); + + after_children_paint(context, phase); +} + +HitTestResult Node::hit_test(const Gfx::IntPoint& position, HitTestType type) const +{ + HitTestResult result; + for_each_child_in_paint_order([&](auto& child) { + auto child_result = child.hit_test(position, type); + if (child_result.layout_node) + result = child_result; + }); + return result; +} + +const Frame& Node::frame() const +{ + ASSERT(document().frame()); + return *document().frame(); +} + +Frame& Node::frame() +{ + ASSERT(document().frame()); + return *document().frame(); +} + +const InitialContainingBlockBox& Node::root() const +{ + ASSERT(document().layout_node()); + return *document().layout_node(); +} + +InitialContainingBlockBox& Node::root() +{ + ASSERT(document().layout_node()); + return *document().layout_node(); +} + +void Node::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode) +{ + for_each_child([&](auto& child) { + child.split_into_lines(context, layout_mode); + }); +} + +void Node::set_needs_display() +{ + if (auto* block = containing_block()) { + block->for_each_fragment([&](auto& fragment) { + if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) { + frame().set_needs_display(enclosing_int_rect(fragment.absolute_rect())); + } + return IterationDecision::Continue; + }); + } +} + +Gfx::FloatPoint Node::box_type_agnostic_position() const +{ + if (is<Box>(*this)) + return downcast<Box>(*this).absolute_position(); + ASSERT(is_inline()); + Gfx::FloatPoint position; + if (auto* block = containing_block()) { + block->for_each_fragment([&](auto& fragment) { + if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) { + position = fragment.absolute_rect().location(); + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + } + return position; +} + +bool Node::is_floating() const +{ + if (!has_style()) + return false; + return computed_values().float_() != CSS::Float::None; +} + +bool Node::is_positioned() const +{ + return has_style() && computed_values().position() != CSS::Position::Static; +} + +bool Node::is_absolutely_positioned() const +{ + if (!has_style()) + return false; + auto position = computed_values().position(); + return position == CSS::Position::Absolute || position == CSS::Position::Fixed; +} + +bool Node::is_fixed_position() const +{ + if (!has_style()) + return false; + auto position = computed_values().position(); + return position == CSS::Position::Fixed; +} + +NodeWithStyle::NodeWithStyle(DOM::Document& document, DOM::Node* node, NonnullRefPtr<CSS::StyleProperties> specified_style) + : Node(document, node) +{ + m_has_style = true; + apply_style(*specified_style); +} + +NodeWithStyle::NodeWithStyle(DOM::Document& document, DOM::Node* node, CSS::ComputedValues computed_values) + : Node(document, node) + , m_computed_values(move(computed_values)) +{ + m_has_style = true; + m_font = Gfx::FontDatabase::default_font(); +} + +void NodeWithStyle::apply_style(const CSS::StyleProperties& specified_style) +{ + auto& computed_values = static_cast<CSS::MutableComputedValues&>(m_computed_values); + + m_font = specified_style.font(); + m_line_height = specified_style.line_height(*this); + + { + // FIXME: This doesn't work right for relative font-sizes + auto length = specified_style.length_or_fallback(CSS::PropertyID::FontSize, CSS::Length(10, CSS::Length::Type::Px)); + m_font_size = length.raw_value(); + } + + auto bgimage = specified_style.property(CSS::PropertyID::BackgroundImage); + if (bgimage.has_value() && bgimage.value()->is_image()) { + m_background_image = static_ptr_cast<CSS::ImageStyleValue>(bgimage.value()); + } + + computed_values.set_display(specified_style.display()); + + auto position = specified_style.position(); + if (position.has_value()) + computed_values.set_position(position.value()); + + auto text_align = specified_style.text_align(); + if (text_align.has_value()) + computed_values.set_text_align(text_align.value()); + + auto white_space = specified_style.white_space(); + if (white_space.has_value()) + computed_values.set_white_space(white_space.value()); + + auto float_ = specified_style.float_(); + if (float_.has_value()) + computed_values.set_float(float_.value()); + + auto clear = specified_style.clear(); + if (clear.has_value()) + computed_values.set_clear(clear.value()); + + auto text_decoration_line = specified_style.text_decoration_line(); + if (text_decoration_line.has_value()) + computed_values.set_text_decoration_line(text_decoration_line.value()); + + auto text_transform = specified_style.text_transform(); + if (text_transform.has_value()) + computed_values.set_text_transform(text_transform.value()); + + if (auto list_style_type = specified_style.list_style_type(); list_style_type.has_value()) + computed_values.set_list_style_type(list_style_type.value()); + + computed_values.set_color(specified_style.color_or_fallback(CSS::PropertyID::Color, document(), Color::Black)); + computed_values.set_background_color(specified_style.color_or_fallback(CSS::PropertyID::BackgroundColor, document(), Color::Transparent)); + + computed_values.set_z_index(specified_style.z_index()); + computed_values.set_width(specified_style.length_or_fallback(CSS::PropertyID::Width, {})); + computed_values.set_min_width(specified_style.length_or_fallback(CSS::PropertyID::MinWidth, {})); + computed_values.set_max_width(specified_style.length_or_fallback(CSS::PropertyID::MaxWidth, {})); + computed_values.set_height(specified_style.length_or_fallback(CSS::PropertyID::Height, {})); + computed_values.set_min_height(specified_style.length_or_fallback(CSS::PropertyID::MinHeight, {})); + computed_values.set_max_height(specified_style.length_or_fallback(CSS::PropertyID::MaxHeight, {})); + + computed_values.set_offset(specified_style.length_box(CSS::PropertyID::Left, CSS::PropertyID::Top, CSS::PropertyID::Right, CSS::PropertyID::Bottom, CSS::Length::make_auto())); + computed_values.set_margin(specified_style.length_box(CSS::PropertyID::MarginLeft, CSS::PropertyID::MarginTop, CSS::PropertyID::MarginRight, CSS::PropertyID::MarginBottom, CSS::Length::make_px(0))); + computed_values.set_padding(specified_style.length_box(CSS::PropertyID::PaddingLeft, CSS::PropertyID::PaddingTop, CSS::PropertyID::PaddingRight, CSS::PropertyID::PaddingBottom, CSS::Length::make_px(0))); + + auto do_border_style = [&](CSS::BorderData& border, CSS::PropertyID width_property, CSS::PropertyID color_property, CSS::PropertyID style_property) { + border.width = specified_style.length_or_fallback(width_property, {}).resolved_or_zero(*this, 0).to_px(*this); + border.color = specified_style.color_or_fallback(color_property, document(), Color::Transparent); + border.line_style = specified_style.line_style(style_property).value_or(CSS::LineStyle::None); + }; + + do_border_style(computed_values.border_left(), CSS::PropertyID::BorderLeftWidth, CSS::PropertyID::BorderLeftColor, CSS::PropertyID::BorderLeftStyle); + do_border_style(computed_values.border_top(), CSS::PropertyID::BorderTopWidth, CSS::PropertyID::BorderTopColor, CSS::PropertyID::BorderTopStyle); + do_border_style(computed_values.border_right(), CSS::PropertyID::BorderRightWidth, CSS::PropertyID::BorderRightColor, CSS::PropertyID::BorderRightStyle); + do_border_style(computed_values.border_bottom(), CSS::PropertyID::BorderBottomWidth, CSS::PropertyID::BorderBottomColor, CSS::PropertyID::BorderBottomStyle); +} + +void Node::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned) +{ +} + +void Node::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned) +{ +} + +void Node::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned) +{ +} + +bool Node::is_root_element() const +{ + if (is_anonymous()) + return false; + return is<HTML::HTMLHtmlElement>(*dom_node()); +} + +String Node::class_name() const +{ + return demangle(typeid(*this).name()); +} + +bool Node::is_inline_block() const +{ + return is_inline() && is<BlockBox>(*this); +} + +NonnullRefPtr<NodeWithStyle> NodeWithStyle::create_anonymous_wrapper() const +{ + auto wrapper = adopt(*new BlockBox(const_cast<DOM::Document&>(document()), nullptr, m_computed_values.clone_inherited_values())); + wrapper->m_font = m_font; + wrapper->m_font_size = m_font_size; + wrapper->m_line_height = m_line_height; + wrapper->m_background_image = m_background_image; + return wrapper; +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/Node.h b/Userland/Libraries/LibWeb/Layout/Node.h new file mode 100644 index 0000000000..c1699924e3 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/Node.h @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2018-2021, 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 <AK/NonnullRefPtr.h> +#include <AK/TypeCasts.h> +#include <AK/Vector.h> +#include <LibGfx/Rect.h> +#include <LibWeb/CSS/ComputedValues.h> +#include <LibWeb/CSS/StyleProperties.h> +#include <LibWeb/Forward.h> +#include <LibWeb/Layout/BoxModelMetrics.h> +#include <LibWeb/Layout/LayoutPosition.h> +#include <LibWeb/Painting/PaintContext.h> +#include <LibWeb/TreeNode.h> + +namespace Web::Layout { + +enum class LayoutMode { + Default, + AllPossibleLineBreaks, + OnlyRequiredLineBreaks, +}; + +enum class PaintPhase { + Background, + Border, + Foreground, + FocusOutline, + Overlay, +}; + +struct HitTestResult { + RefPtr<Node> layout_node; + int index_in_node { 0 }; + + enum InternalPosition { + None, + Before, + Inside, + After, + }; + InternalPosition internal_position { None }; +}; + +enum class HitTestType { + Exact, // Exact matches only + TextCursor, // Clicking past the right/bottom edge of text will still hit the text +}; + +class Node : public TreeNode<Node> { +public: + virtual ~Node(); + + virtual HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const; + + bool is_anonymous() const { return !m_dom_node; } + const DOM::Node* dom_node() const { return m_dom_node; } + DOM::Node* dom_node() { return m_dom_node; } + + DOM::Document& document() { return m_document; } + const DOM::Document& document() const { return m_document; } + + const Frame& frame() const; + Frame& frame(); + + const InitialContainingBlockBox& root() const; + InitialContainingBlockBox& root(); + + bool is_root_element() const; + + String class_name() const; + + bool has_style() const { return m_has_style; } + + virtual bool can_have_children() const { return true; } + + bool is_inline() const { return m_inline; } + void set_inline(bool b) { m_inline = b; } + + bool is_inline_block() const; + + virtual bool wants_mouse_events() const { return false; } + + virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers); + virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers); + virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers); + + virtual void before_children_paint(PaintContext&, PaintPhase) {}; + virtual void paint(PaintContext&, PaintPhase); + virtual void paint_fragment(PaintContext&, const LineBoxFragment&, PaintPhase) const { } + virtual void after_children_paint(PaintContext&, PaintPhase) {}; + + virtual bool is_box() const { return false; } + + bool is_floating() const; + bool is_positioned() const; + bool is_absolutely_positioned() const; + bool is_fixed_position() const; + + const BlockBox* containing_block() const; + BlockBox* containing_block() { return const_cast<BlockBox*>(const_cast<const Node*>(this)->containing_block()); } + + bool can_contain_boxes_with_position_absolute() const; + + const Gfx::Font& font() const; + const CSS::ImmutableComputedValues& computed_values() const; + + NodeWithStyle* parent(); + const NodeWithStyle* parent() const; + + void inserted_into(Node&) { } + void removed_from(Node&) { } + void children_changed() { } + + virtual void split_into_lines(InlineFormattingContext&, LayoutMode); + + bool is_visible() const { return m_visible; } + void set_visible(bool visible) { m_visible = visible; } + + virtual void set_needs_display(); + + bool children_are_inline() const { return m_children_are_inline; } + void set_children_are_inline(bool value) { m_children_are_inline = value; } + + Gfx::FloatPoint box_type_agnostic_position() const; + + float font_size() const; + + enum class SelectionState { + None, // No selection + Start, // Selection starts in this Node + End, // Selection ends in this Node + StartAndEnd, // Selection starts and ends in this Node + Full, // Selection starts before and ends after this Node + }; + + SelectionState selection_state() const { return m_selection_state; } + void set_selection_state(SelectionState state) { m_selection_state = state; } + + template<typename Callback> + void for_each_child_in_paint_order(Callback callback) const + { + for_each_child([&](auto& child) { + if (is<Box>(child) && downcast<Box>(child).stacking_context()) + return; + if (!child.is_positioned()) + callback(child); + }); + for_each_child([&](auto& child) { + if (is<Box>(child) && downcast<Box>(child).stacking_context()) + return; + if (child.is_positioned()) + callback(child); + }); + } + +protected: + Node(DOM::Document&, DOM::Node*); + +private: + friend class NodeWithStyle; + + NonnullRefPtr<DOM::Document> m_document; + RefPtr<DOM::Node> m_dom_node; + + bool m_inline { false }; + bool m_has_style { false }; + bool m_visible { true }; + bool m_children_are_inline { false }; + SelectionState m_selection_state { SelectionState::None }; +}; + +class NodeWithStyle : public Node { +public: + virtual ~NodeWithStyle() override { } + + const CSS::ImmutableComputedValues& computed_values() const { return static_cast<const CSS::ImmutableComputedValues&>(m_computed_values); } + + void apply_style(const CSS::StyleProperties&); + + const Gfx::Font& font() const { return *m_font; } + float line_height() const { return m_line_height; } + float font_size() const { return m_font_size; } + const CSS::ImageStyleValue* background_image() const { return m_background_image; } + + NonnullRefPtr<NodeWithStyle> create_anonymous_wrapper() const; + +protected: + NodeWithStyle(DOM::Document&, DOM::Node*, NonnullRefPtr<CSS::StyleProperties>); + NodeWithStyle(DOM::Document&, DOM::Node*, CSS::ComputedValues); + +private: + CSS::ComputedValues m_computed_values; + RefPtr<Gfx::Font> m_font; + float m_line_height { 0 }; + float m_font_size { 0 }; + RefPtr<CSS::ImageStyleValue> m_background_image; + + CSS::Position m_position; +}; + +class NodeWithStyleAndBoxModelMetrics : public NodeWithStyle { +public: + BoxModelMetrics& box_model() { return m_box_model; } + const BoxModelMetrics& box_model() const { return m_box_model; } + +protected: + NodeWithStyleAndBoxModelMetrics(DOM::Document& document, DOM::Node* node, NonnullRefPtr<CSS::StyleProperties> style) + : NodeWithStyle(document, node, move(style)) + { + } + + NodeWithStyleAndBoxModelMetrics(DOM::Document& document, DOM::Node* node, CSS::ComputedValues computed_values) + : NodeWithStyle(document, node, move(computed_values)) + { + } + +private: + BoxModelMetrics m_box_model; +}; + +inline const Gfx::Font& Node::font() const +{ + if (m_has_style) + return static_cast<const NodeWithStyle*>(this)->font(); + return parent()->font(); +} + +inline float Node::font_size() const +{ + if (m_has_style) + return static_cast<const NodeWithStyle*>(this)->font_size(); + return parent()->font_size(); +} + +inline const CSS::ImmutableComputedValues& Node::computed_values() const +{ + if (m_has_style) + return static_cast<const NodeWithStyle*>(this)->computed_values(); + return parent()->computed_values(); +} + +inline const NodeWithStyle* Node::parent() const +{ + return static_cast<const NodeWithStyle*>(TreeNode<Node>::parent()); +} + +inline NodeWithStyle* Node::parent() +{ + return static_cast<NodeWithStyle*>(TreeNode<Node>::parent()); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/ReplacedBox.cpp b/Userland/Libraries/LibWeb/Layout/ReplacedBox.cpp new file mode 100644 index 0000000000..2ea63bb233 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/ReplacedBox.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-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 <LibWeb/DOM/Element.h> +#include <LibWeb/Layout/BlockBox.h> +#include <LibWeb/Layout/InlineFormattingContext.h> +#include <LibWeb/Layout/ReplacedBox.h> + +namespace Web::Layout { + +ReplacedBox::ReplacedBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style) + : Box(document, &element, move(style)) +{ + // FIXME: Allow non-inline replaced elements. + set_inline(true); +} + +ReplacedBox::~ReplacedBox() +{ +} + +void ReplacedBox::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode) +{ + auto& containing_block = context.containing_block(); + + prepare_for_replaced_layout(); + context.dimension_box_on_line(*this, layout_mode); + + auto* line_box = &containing_block.ensure_last_line_box(); + if (line_box->width() > 0 && line_box->width() + width() > context.available_width_at_line(containing_block.line_boxes().size() - 1)) + line_box = &containing_block.add_line_box(); + line_box->add_fragment(*this, 0, 0, width(), height()); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/ReplacedBox.h b/Userland/Libraries/LibWeb/Layout/ReplacedBox.h new file mode 100644 index 0000000000..f1da91a197 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/ReplacedBox.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-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/DOM/Element.h> +#include <LibWeb/Layout/Box.h> + +namespace Web::Layout { + +class ReplacedBox : public Box { +public: + ReplacedBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>); + virtual ~ReplacedBox() override; + + const DOM::Element& dom_node() const { return downcast<DOM::Element>(*Node::dom_node()); } + DOM::Element& dom_node() { return downcast<DOM::Element>(*Node::dom_node()); } + + bool has_intrinsic_width() const { return m_has_intrinsic_width; } + bool has_intrinsic_height() const { return m_has_intrinsic_height; } + bool has_intrinsic_ratio() const { return m_has_intrinsic_ratio; } + + float intrinsic_width() const { return m_intrinsic_width; } + float intrinsic_height() const { return m_intrinsic_height; } + float intrinsic_ratio() const { return m_intrinsic_ratio; } + + void set_has_intrinsic_width(bool has) { m_has_intrinsic_width = has; } + void set_has_intrinsic_height(bool has) { m_has_intrinsic_height = has; } + void set_has_intrinsic_ratio(bool has) { m_has_intrinsic_ratio = has; } + + void set_intrinsic_width(float width) { m_intrinsic_width = width; } + void set_intrinsic_height(float height) { m_intrinsic_height = height; } + void set_intrinsic_ratio(float ratio) { m_intrinsic_ratio = ratio; } + + virtual void prepare_for_replaced_layout() { } + + virtual bool can_have_children() const override { return false; } + +protected: + virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override; + +private: + bool m_has_intrinsic_width { false }; + bool m_has_intrinsic_height { false }; + bool m_has_intrinsic_ratio { false }; + float m_intrinsic_width { 0 }; + float m_intrinsic_height { 0 }; + float m_intrinsic_ratio { 0 }; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/SVGBox.cpp b/Userland/Libraries/LibWeb/Layout/SVGBox.cpp new file mode 100644 index 0000000000..8370d4b9bd --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/SVGBox.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibGUI/Painter.h> +#include <LibGfx/Font.h> +#include <LibGfx/StylePainter.h> +#include <LibWeb/Layout/SVGBox.h> + +namespace Web::Layout { + +SVGBox::SVGBox(DOM::Document& document, SVG::SVGElement& element, NonnullRefPtr<CSS::StyleProperties> style) + : ReplacedBox(document, element, move(style)) +{ +} + +void SVGBox::before_children_paint(PaintContext& context, PaintPhase phase) +{ + Node::before_children_paint(context, phase); + if (phase != PaintPhase::Foreground) + return; + context.svg_context().save(); +} + +void SVGBox::after_children_paint(PaintContext& context, PaintPhase phase) +{ + Node::after_children_paint(context, phase); + if (phase != PaintPhase::Foreground) + return; + context.svg_context().restore(); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/SVGBox.h b/Userland/Libraries/LibWeb/Layout/SVGBox.h new file mode 100644 index 0000000000..dce2ee6f64 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/SVGBox.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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/Layout/ReplacedBox.h> +#include <LibWeb/SVG/SVGElement.h> +#include <LibWeb/SVG/SVGGraphicsElement.h> + +namespace Web::Layout { + +class SVGBox : public ReplacedBox { +public: + SVGBox(DOM::Document&, SVG::SVGElement&, NonnullRefPtr<CSS::StyleProperties>); + virtual ~SVGBox() override = default; + + virtual void before_children_paint(PaintContext& context, PaintPhase phase) override; + virtual void after_children_paint(PaintContext& context, PaintPhase phase) override; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.cpp b/Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.cpp new file mode 100644 index 0000000000..be1f9541d4 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibWeb/Layout/SVGGraphicsBox.h> + +namespace Web::Layout { + +SVGGraphicsBox::SVGGraphicsBox(DOM::Document& document, SVG::SVGGraphicsElement& element, NonnullRefPtr<CSS::StyleProperties> properties) + : SVGBox(document, element, properties) +{ +} + +void SVGGraphicsBox::before_children_paint(PaintContext& context, PaintPhase phase) +{ + SVGBox::before_children_paint(context, phase); + if (phase != PaintPhase::Foreground) + return; + + auto& graphics_element = downcast<SVG::SVGGraphicsElement>(dom_node()); + + if (graphics_element.fill_color().has_value()) + context.svg_context().set_fill_color(graphics_element.fill_color().value()); + if (graphics_element.stroke_color().has_value()) + context.svg_context().set_stroke_color(graphics_element.stroke_color().value()); + if (graphics_element.stroke_width().has_value()) + context.svg_context().set_stroke_width(graphics_element.stroke_width().value()); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.h b/Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.h new file mode 100644 index 0000000000..10dbadef07 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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/Layout/SVGBox.h> +#include <LibWeb/SVG/SVGElement.h> +#include <LibWeb/SVG/SVGGraphicsElement.h> + +namespace Web::Layout { + +class SVGGraphicsBox : public SVGBox { +public: + SVGGraphicsBox(DOM::Document&, SVG::SVGGraphicsElement&, NonnullRefPtr<CSS::StyleProperties>); + virtual ~SVGGraphicsBox() override = default; + + virtual void before_children_paint(PaintContext& context, PaintPhase phase) override; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/SVGPathBox.cpp b/Userland/Libraries/LibWeb/Layout/SVGPathBox.cpp new file mode 100644 index 0000000000..87879043eb --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/SVGPathBox.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibGfx/Painter.h> +#include <LibWeb/Layout/SVGPathBox.h> +#include <LibWeb/SVG/SVGPathElement.h> + +namespace Web::Layout { + +SVGPathBox::SVGPathBox(DOM::Document& document, SVG::SVGPathElement& element, NonnullRefPtr<CSS::StyleProperties> properties) + : SVGGraphicsBox(document, element, properties) +{ +} + +void SVGPathBox::prepare_for_replaced_layout() +{ + auto& bounding_box = dom_node().get_path().bounding_box(); + set_has_intrinsic_width(true); + set_has_intrinsic_height(true); + set_intrinsic_width(bounding_box.width()); + set_intrinsic_height(bounding_box.height()); + + // FIXME: This does not belong here! Someone at a higher level should place this box. + set_offset(bounding_box.top_left()); +} + +void SVGPathBox::paint(PaintContext& context, PaintPhase phase) +{ + if (!is_visible()) + return; + + SVGGraphicsBox::paint(context, phase); + + if (phase != PaintPhase::Foreground) + return; + + auto& path_element = dom_node(); + auto& path = path_element.get_path(); + + // We need to fill the path before applying the stroke, however the filled + // path must be closed, whereas the stroke path may not necessary be closed. + // Copy the path and close it for filling, but use the previous path for stroke + auto closed_path = path; + closed_path.close(); + + // Fills are computed as though all paths are closed (https://svgwg.org/svg2-draft/painting.html#FillProperties) + auto& painter = context.painter(); + auto& svg_context = context.svg_context(); + + auto offset = (absolute_position() - effective_offset()).to_type<int>(); + + painter.translate(offset); + + painter.fill_path( + closed_path, + path_element.fill_color().value_or(svg_context.fill_color()), + Gfx::Painter::WindingRule::EvenOdd); + painter.stroke_path( + path, + path_element.stroke_color().value_or(svg_context.stroke_color()), + path_element.stroke_width().value_or(svg_context.stroke_width())); + + painter.translate(-offset); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/SVGPathBox.h b/Userland/Libraries/LibWeb/Layout/SVGPathBox.h new file mode 100644 index 0000000000..cbfe94b4ec --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/SVGPathBox.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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/Layout/SVGGraphicsBox.h> + +namespace Web::Layout { + +class SVGPathBox final : public SVGGraphicsBox { +public: + SVGPathBox(DOM::Document&, SVG::SVGPathElement&, NonnullRefPtr<CSS::StyleProperties>); + virtual ~SVGPathBox() override = default; + + SVG::SVGPathElement& dom_node() { return downcast<SVG::SVGPathElement>(SVGGraphicsBox::dom_node()); } + + virtual void prepare_for_replaced_layout() override; + virtual void paint(PaintContext& context, PaintPhase phase) override; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp b/Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp new file mode 100644 index 0000000000..af4a038dbc --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibWeb/Layout/SVGSVGBox.h> + +namespace Web::Layout { + +SVGSVGBox::SVGSVGBox(DOM::Document& document, SVG::SVGSVGElement& element, NonnullRefPtr<CSS::StyleProperties> properties) + : SVGGraphicsBox(document, element, properties) +{ +} + +void SVGSVGBox::prepare_for_replaced_layout() +{ + set_has_intrinsic_width(true); + set_has_intrinsic_height(true); + set_intrinsic_width(dom_node().width()); + set_intrinsic_height(dom_node().height()); +} + +void SVGSVGBox::before_children_paint(PaintContext& context, PaintPhase phase) +{ + if (phase != PaintPhase::Foreground) + return; + + if (!context.has_svg_context()) + context.set_svg_context(SVGContext()); + + SVGGraphicsBox::before_children_paint(context, phase); +} + +void SVGSVGBox::after_children_paint(PaintContext& context, PaintPhase phase) +{ + SVGGraphicsBox::after_children_paint(context, phase); + if (phase != PaintPhase::Foreground) + return; +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/SVGSVGBox.h b/Userland/Libraries/LibWeb/Layout/SVGSVGBox.h new file mode 100644 index 0000000000..a95bf11f69 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/SVGSVGBox.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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/Layout/SVGGraphicsBox.h> +#include <LibWeb/SVG/SVGSVGElement.h> + +namespace Web::Layout { + +class SVGSVGBox final : public SVGGraphicsBox { +public: + SVGSVGBox(DOM::Document&, SVG::SVGSVGElement&, NonnullRefPtr<CSS::StyleProperties>); + virtual ~SVGSVGBox() override = default; + + SVG::SVGSVGElement& dom_node() { return downcast<SVG::SVGSVGElement>(SVGGraphicsBox::dom_node()); } + + virtual void prepare_for_replaced_layout() override; + + virtual void before_children_paint(PaintContext& context, PaintPhase phase) override; + virtual void after_children_paint(PaintContext& context, PaintPhase phase) override; + + virtual bool can_have_children() const override { return true; } +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/TableBox.cpp b/Userland/Libraries/LibWeb/Layout/TableBox.cpp new file mode 100644 index 0000000000..66fdfb8bd3 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/TableBox.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-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 <LibWeb/DOM/Element.h> +#include <LibWeb/Layout/TableBox.h> + +namespace Web::Layout { + +TableBox::TableBox(DOM::Document& document, DOM::Element* element, NonnullRefPtr<CSS::StyleProperties> style) + : Layout::BlockBox(document, element, move(style)) +{ +} + +TableBox::TableBox(DOM::Document& document, DOM::Element* element, CSS::ComputedValues computed_values) + : Layout::BlockBox(document, element, move(computed_values)) +{ +} + +TableBox::~TableBox() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/TableBox.h b/Userland/Libraries/LibWeb/Layout/TableBox.h new file mode 100644 index 0000000000..cff6af9222 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/TableBox.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018-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/Layout/BlockBox.h> + +namespace Web::Layout { + +class TableBox final : public Layout::BlockBox { +public: + TableBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>); + TableBox(DOM::Document&, DOM::Element*, CSS::ComputedValues); + virtual ~TableBox() override; + + static CSS::Display static_display() { return CSS::Display::Table; } +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/TableCellBox.cpp b/Userland/Libraries/LibWeb/Layout/TableCellBox.cpp new file mode 100644 index 0000000000..197fe59b87 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/TableCellBox.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-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 <LibWeb/DOM/Element.h> +#include <LibWeb/Layout/TableCellBox.h> +#include <LibWeb/Layout/TableRowBox.h> + +namespace Web::Layout { + +TableCellBox::TableCellBox(DOM::Document& document, DOM::Element* element, NonnullRefPtr<CSS::StyleProperties> style) + : Layout::BlockBox(document, element, move(style)) +{ +} + +TableCellBox::TableCellBox(DOM::Document& document, DOM::Element* element, CSS::ComputedValues computed_values) + : Layout::BlockBox(document, element, move(computed_values)) +{ +} + +TableCellBox::~TableCellBox() +{ +} + +size_t TableCellBox::colspan() const +{ + if (!dom_node()) + return 0; + return downcast<DOM::Element>(*dom_node()).attribute(HTML::AttributeNames::colspan).to_uint().value_or(1); +} + +float TableCellBox::width_of_logical_containing_block() const +{ + if (auto* row = first_ancestor_of_type<TableRowBox>()) + return row->width(); + return 0; +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/TableCellBox.h b/Userland/Libraries/LibWeb/Layout/TableCellBox.h new file mode 100644 index 0000000000..091a4ae6d6 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/TableCellBox.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-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/Layout/BlockBox.h> + +namespace Web::Layout { + +class TableCellBox final : public BlockBox { +public: + TableCellBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>); + TableCellBox(DOM::Document&, DOM::Element*, CSS::ComputedValues); + virtual ~TableCellBox() override; + + TableCellBox* next_cell() { return next_sibling_of_type<TableCellBox>(); } + const TableCellBox* next_cell() const { return next_sibling_of_type<TableCellBox>(); } + + size_t colspan() const; + + static CSS::Display static_display() { return CSS::Display::TableCell; } + +private: + virtual float width_of_logical_containing_block() const override; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp new file mode 100644 index 0000000000..865cef64e6 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp @@ -0,0 +1,133 @@ +/* + * 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 <LibWeb/CSS/Length.h> +#include <LibWeb/DOM/Node.h> +#include <LibWeb/Layout/BlockBox.h> +#include <LibWeb/Layout/Box.h> +#include <LibWeb/Layout/InlineFormattingContext.h> +#include <LibWeb/Layout/TableBox.h> +#include <LibWeb/Layout/TableCellBox.h> +#include <LibWeb/Layout/TableFormattingContext.h> +#include <LibWeb/Layout/TableRowBox.h> +#include <LibWeb/Layout/TableRowGroupBox.h> +#include <LibWeb/Page/Frame.h> + +namespace Web::Layout { + +TableFormattingContext::TableFormattingContext(Box& context_box, FormattingContext* parent) + : BlockFormattingContext(context_box, parent) +{ +} + +TableFormattingContext::~TableFormattingContext() +{ +} + +void TableFormattingContext::run(Box& box, LayoutMode) +{ + compute_width(box); + + float total_content_height = 0; + + box.for_each_child_of_type<TableRowGroupBox>([&](auto& row_group_box) { + compute_width(row_group_box); + auto column_count = row_group_box.column_count(); + Vector<float> column_widths; + column_widths.resize(column_count); + + row_group_box.template for_each_child_of_type<TableRowBox>([&](auto& row) { + calculate_column_widths(row, column_widths); + }); + + float content_height = 0; + + row_group_box.template for_each_child_of_type<TableRowBox>([&](auto& row) { + row.set_offset(0, content_height); + layout_row(row, column_widths); + content_height += row.height(); + }); + + row_group_box.set_height(content_height); + + total_content_height += content_height; + }); + + // FIXME: This is a total hack, we should respect the 'height' property. + box.set_height(total_content_height); +} + +void TableFormattingContext::calculate_column_widths(Box& row, Vector<float>& column_widths) +{ + size_t column_index = 0; + auto* table = row.first_ancestor_of_type<TableBox>(); + bool use_auto_layout = !table || table->computed_values().width().is_undefined_or_auto(); + row.for_each_child_of_type<TableCellBox>([&](auto& cell) { + compute_width(cell); + if (use_auto_layout) { + layout_inside(cell, LayoutMode::OnlyRequiredLineBreaks); + } else { + layout_inside(cell, LayoutMode::Default); + } + column_widths[column_index] = max(column_widths[column_index], cell.width()); + column_index += cell.colspan(); + }); +} + +void TableFormattingContext::layout_row(Box& row, Vector<float>& column_widths) +{ + size_t column_index = 0; + float tallest_cell_height = 0; + float content_width = 0; + auto* table = row.first_ancestor_of_type<TableBox>(); + bool use_auto_layout = !table || table->computed_values().width().is_undefined_or_auto(); + + row.for_each_child_of_type<TableCellBox>([&](auto& cell) { + cell.set_offset(row.effective_offset().translated(content_width, 0)); + + // Layout the cell contents a second time, now that we know its final width. + if (use_auto_layout) { + layout_inside(cell, LayoutMode::OnlyRequiredLineBreaks); + } else { + layout_inside(cell, LayoutMode::Default); + } + + size_t cell_colspan = cell.colspan(); + for (size_t i = 0; i < cell_colspan; ++i) + content_width += column_widths[column_index++]; + tallest_cell_height = max(tallest_cell_height, cell.height()); + }); + + if (use_auto_layout) { + row.set_width(content_width); + } else { + row.set_width(table->width()); + } + + row.set_height(tallest_cell_height); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h new file mode 100644 index 0000000000..7bb973a5d2 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h @@ -0,0 +1,46 @@ +/* + * 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 <AK/Forward.h> +#include <LibWeb/Layout/BlockFormattingContext.h> + +namespace Web::Layout { + +class TableFormattingContext final : public BlockFormattingContext { +public: + explicit TableFormattingContext(Box&, FormattingContext* parent); + ~TableFormattingContext(); + + virtual void run(Box&, LayoutMode) override; + +private: + void calculate_column_widths(Box& row, Vector<float>& column_widths); + void layout_row(Box& row, Vector<float>& column_widths); +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/TableRowBox.cpp b/Userland/Libraries/LibWeb/Layout/TableRowBox.cpp new file mode 100644 index 0000000000..b3033adb1c --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/TableRowBox.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-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 <LibWeb/DOM/Element.h> +#include <LibWeb/Layout/TableRowBox.h> + +namespace Web::Layout { + +TableRowBox::TableRowBox(DOM::Document& document, DOM::Element* element, NonnullRefPtr<CSS::StyleProperties> style) + : Box(document, element, move(style)) +{ +} + +TableRowBox::TableRowBox(DOM::Document& document, DOM::Element* element, CSS::ComputedValues computed_values) + : Box(document, element, move(computed_values)) +{ +} + +TableRowBox::~TableRowBox() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/TableRowBox.h b/Userland/Libraries/LibWeb/Layout/TableRowBox.h new file mode 100644 index 0000000000..eab602c156 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/TableRowBox.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018-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/Layout/Box.h> + +namespace Web::Layout { + +class TableRowBox final : public Box { +public: + TableRowBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>); + TableRowBox(DOM::Document&, DOM::Element*, CSS::ComputedValues); + virtual ~TableRowBox() override; + + static CSS::Display static_display() { return CSS::Display::TableRow; } +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/TableRowGroupBox.cpp b/Userland/Libraries/LibWeb/Layout/TableRowGroupBox.cpp new file mode 100644 index 0000000000..f6ced13a33 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/TableRowGroupBox.cpp @@ -0,0 +1,56 @@ +/* + * 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 <LibWeb/DOM/Element.h> +#include <LibWeb/Layout/TableCellBox.h> +#include <LibWeb/Layout/TableRowBox.h> +#include <LibWeb/Layout/TableRowGroupBox.h> + +namespace Web::Layout { + +TableRowGroupBox::TableRowGroupBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style) + : Layout::BlockBox(document, &element, move(style)) +{ +} + +TableRowGroupBox::~TableRowGroupBox() +{ +} + +size_t TableRowGroupBox::column_count() const +{ + size_t table_column_count = 0; + for_each_child_of_type<TableRowBox>([&](auto& row) { + size_t row_column_count = 0; + row.template for_each_child_of_type<TableCellBox>([&](auto& cell) { + row_column_count += cell.colspan(); + }); + table_column_count = max(table_column_count, row_column_count); + }); + return table_column_count; +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/TableRowGroupBox.h b/Userland/Libraries/LibWeb/Layout/TableRowGroupBox.h new file mode 100644 index 0000000000..6a9fd6956c --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/TableRowGroupBox.h @@ -0,0 +1,41 @@ +/* + * 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/Layout/BlockBox.h> + +namespace Web::Layout { + +class TableRowGroupBox final : public BlockBox { +public: + TableRowGroupBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>); + virtual ~TableRowGroupBox() override; + + size_t column_count() const; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/TextNode.cpp b/Userland/Libraries/LibWeb/Layout/TextNode.cpp new file mode 100644 index 0000000000..bf027b9cb7 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/TextNode.cpp @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2018-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/StringBuilder.h> +#include <AK/Utf8View.h> +#include <LibCore/DirIterator.h> +#include <LibGUI/Painter.h> +#include <LibGfx/Font.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/Layout/BlockBox.h> +#include <LibWeb/Layout/InlineFormattingContext.h> +#include <LibWeb/Layout/TextNode.h> +#include <LibWeb/Page/Frame.h> +#include <ctype.h> + +namespace Web::Layout { + +TextNode::TextNode(DOM::Document& document, DOM::Text& text) + : Node(document, &text) +{ + set_inline(true); +} + +TextNode::~TextNode() +{ +} + +static bool is_all_whitespace(const StringView& string) +{ + for (size_t i = 0; i < string.length(); ++i) { + if (!isspace(string[i])) + return false; + } + return true; +} + +void TextNode::paint_fragment(PaintContext& context, const LineBoxFragment& fragment, PaintPhase phase) const +{ + auto& painter = context.painter(); + + if (phase == PaintPhase::Background) { + painter.fill_rect(enclosing_int_rect(fragment.absolute_rect()), computed_values().background_color()); + } + + if (phase == PaintPhase::Foreground) { + painter.set_font(font()); + + if (document().inspected_node() == &dom_node()) + context.painter().draw_rect(enclosing_int_rect(fragment.absolute_rect()), Color::Magenta); + + if (computed_values().text_decoration_line() == CSS::TextDecorationLine::Underline) + painter.draw_line(enclosing_int_rect(fragment.absolute_rect()).bottom_left().translated(0, 1), enclosing_int_rect(fragment.absolute_rect()).bottom_right().translated(0, 1), computed_values().color()); + + // FIXME: text-transform should be done already in layout, since uppercase glyphs may be wider than lowercase, etc. + auto text = m_text_for_rendering; + auto text_transform = computed_values().text_transform(); + if (text_transform == CSS::TextTransform::Uppercase) + text = m_text_for_rendering.to_uppercase(); + if (text_transform == CSS::TextTransform::Lowercase) + text = m_text_for_rendering.to_lowercase(); + + painter.draw_text(enclosing_int_rect(fragment.absolute_rect()), text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::CenterLeft, computed_values().color()); + + auto selection_rect = fragment.selection_rect(font()); + if (!selection_rect.is_empty()) { + painter.fill_rect(enclosing_int_rect(selection_rect), context.palette().selection()); + Gfx::PainterStateSaver saver(painter); + painter.add_clip_rect(enclosing_int_rect(selection_rect)); + painter.draw_text(enclosing_int_rect(fragment.absolute_rect()), text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::CenterLeft, context.palette().selection_text()); + } + + paint_cursor_if_needed(context, fragment); + } +} + +void TextNode::paint_cursor_if_needed(PaintContext& context, const LineBoxFragment& fragment) const +{ + if (!frame().is_focused_frame()) + return; + + if (!frame().cursor_blink_state()) + return; + + if (frame().cursor_position().node() != &dom_node()) + return; + + if (!(frame().cursor_position().offset() >= (unsigned)fragment.start() && frame().cursor_position().offset() < (unsigned)(fragment.start() + fragment.length()))) + return; + + if (!fragment.layout_node().dom_node() || !fragment.layout_node().dom_node()->is_editable()) + return; + + auto fragment_rect = fragment.absolute_rect(); + + float cursor_x = fragment_rect.x() + font().width(fragment.text().substring_view(0, frame().cursor_position().offset() - fragment.start())); + float cursor_top = fragment_rect.top(); + float cursor_height = fragment_rect.height(); + Gfx::IntRect cursor_rect(cursor_x, cursor_top, 1, cursor_height); + + context.painter().draw_rect(cursor_rect, computed_values().color()); +} + +template<typename Callback> +void TextNode::for_each_chunk(Callback callback, LayoutMode layout_mode, bool do_wrap_lines, bool do_wrap_breaks) const +{ + Utf8View view(m_text_for_rendering); + if (view.is_empty()) + return; + + auto start_of_chunk = view.begin(); + + auto commit_chunk = [&](auto it, bool has_breaking_newline, bool must_commit = false) { + if (layout_mode == LayoutMode::OnlyRequiredLineBreaks && !must_commit) + return; + + int start = view.byte_offset_of(start_of_chunk); + int length = view.byte_offset_of(it) - view.byte_offset_of(start_of_chunk); + + if (has_breaking_newline || length > 0) { + auto chunk_view = view.substring_view(start, length); + callback(chunk_view, start, length, has_breaking_newline, is_all_whitespace(chunk_view.as_string())); + } + + start_of_chunk = it; + }; + + bool last_was_space = isspace(*view.begin()); + bool last_was_newline = false; + for (auto it = view.begin(); it != view.end();) { + if (layout_mode == LayoutMode::AllPossibleLineBreaks) { + commit_chunk(it, false); + } + if (last_was_newline) { + last_was_newline = false; + commit_chunk(it, true); + } + if (do_wrap_breaks && *it == '\n') { + last_was_newline = true; + commit_chunk(it, false); + } + if (do_wrap_lines) { + bool is_space = isspace(*it); + if (is_space != last_was_space) { + last_was_space = is_space; + commit_chunk(it, false); + } + } + ++it; + } + if (last_was_newline) + commit_chunk(view.end(), true); + if (start_of_chunk != view.end()) + commit_chunk(view.end(), false, true); +} + +void TextNode::split_into_lines_by_rules(InlineFormattingContext& context, LayoutMode layout_mode, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks) +{ + auto& containing_block = context.containing_block(); + + auto& font = this->font(); + + auto& line_boxes = containing_block.line_boxes(); + containing_block.ensure_last_line_box(); + float available_width = context.available_width_at_line(line_boxes.size() - 1) - line_boxes.last().width(); + + // Collapse whitespace into single spaces + if (do_collapse) { + auto utf8_view = Utf8View(dom_node().data()); + StringBuilder builder(dom_node().data().length()); + auto it = utf8_view.begin(); + auto skip_over_whitespace = [&] { + auto prev = it; + while (it != utf8_view.end() && isspace(*it)) { + prev = it; + ++it; + } + it = prev; + }; + if (line_boxes.last().is_empty_or_ends_in_whitespace()) + skip_over_whitespace(); + for (; it != utf8_view.end(); ++it) { + if (!isspace(*it)) { + builder.append(utf8_view.as_string().characters_without_null_termination() + utf8_view.byte_offset_of(it), it.code_point_length_in_bytes()); + } else { + builder.append(' '); + skip_over_whitespace(); + } + } + m_text_for_rendering = builder.to_string(); + } else { + m_text_for_rendering = dom_node().data(); + } + + // do_wrap_lines => chunks_are_words + // !do_wrap_lines => chunks_are_lines + struct Chunk { + Utf8View view; + int start { 0 }; + int length { 0 }; + bool is_break { false }; + bool is_all_whitespace { false }; + }; + Vector<Chunk, 128> chunks; + + for_each_chunk( + [&](const Utf8View& view, int start, int length, bool is_break, bool is_all_whitespace) { + chunks.append({ Utf8View(view), start, length, is_break, is_all_whitespace }); + }, + layout_mode, do_wrap_lines, do_wrap_breaks); + + for (size_t i = 0; i < chunks.size(); ++i) { + auto& chunk = chunks[i]; + + // Collapse entire fragment into non-existence if previous fragment on line ended in whitespace. + if (do_collapse && line_boxes.last().is_empty_or_ends_in_whitespace() && chunk.is_all_whitespace) + continue; + + float chunk_width; + if (do_wrap_lines) { + if (do_collapse && isspace(*chunk.view.begin()) && line_boxes.last().is_empty_or_ends_in_whitespace()) { + // This is a non-empty chunk that starts with collapsible whitespace. + // We are at either at the start of a new line, or after something that ended in whitespace, + // so we don't need to contribute our own whitespace to the line. Skip over it instead! + ++chunk.start; + --chunk.length; + chunk.view = chunk.view.substring_view(1, chunk.view.byte_length() - 1); + } + + chunk_width = font.width(chunk.view) + font.glyph_spacing(); + + if (line_boxes.last().width() > 0 && chunk_width > available_width) { + containing_block.add_line_box(); + available_width = context.available_width_at_line(line_boxes.size() - 1); + + if (do_collapse && chunk.is_all_whitespace) + continue; + } + } else { + chunk_width = font.width(chunk.view); + } + + line_boxes.last().add_fragment(*this, chunk.start, chunk.length, chunk_width, font.glyph_height()); + available_width -= chunk_width; + + if (do_wrap_lines) { + if (available_width < 0) { + containing_block.add_line_box(); + available_width = context.available_width_at_line(line_boxes.size() - 1); + } + } + + if (do_wrap_breaks) { + if (chunk.is_break) { + containing_block.add_line_box(); + available_width = context.available_width_at_line(line_boxes.size() - 1); + } + } + } +} + +void TextNode::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode) +{ + bool do_collapse = true; + bool do_wrap_lines = true; + bool do_wrap_breaks = false; + + if (computed_values().white_space() == CSS::WhiteSpace::Nowrap) { + do_collapse = true; + do_wrap_lines = false; + do_wrap_breaks = false; + } else if (computed_values().white_space() == CSS::WhiteSpace::Pre) { + do_collapse = false; + do_wrap_lines = false; + do_wrap_breaks = true; + } else if (computed_values().white_space() == CSS::WhiteSpace::PreLine) { + do_collapse = true; + do_wrap_lines = true; + do_wrap_breaks = true; + } else if (computed_values().white_space() == CSS::WhiteSpace::PreWrap) { + do_collapse = false; + do_wrap_lines = true; + do_wrap_breaks = true; + } + + split_into_lines_by_rules(context, layout_mode, do_collapse, do_wrap_lines, do_wrap_breaks); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/TextNode.h b/Userland/Libraries/LibWeb/Layout/TextNode.h new file mode 100644 index 0000000000..03781a22b2 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/TextNode.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018-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/DOM/Text.h> +#include <LibWeb/Layout/Node.h> + +namespace Web::Layout { + +class LineBoxFragment; + +class TextNode : public Node { +public: + TextNode(DOM::Document&, DOM::Text&); + virtual ~TextNode() override; + + const DOM::Text& dom_node() const { return static_cast<const DOM::Text&>(*Node::dom_node()); } + + const String& text_for_rendering() const { return m_text_for_rendering; } + + virtual void paint_fragment(PaintContext&, const LineBoxFragment&, PaintPhase) const override; + + virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override; + +private: + void split_into_lines_by_rules(InlineFormattingContext&, LayoutMode, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks); + void paint_cursor_if_needed(PaintContext&, const LineBoxFragment&) const; + + template<typename Callback> + void for_each_chunk(Callback, LayoutMode, bool do_wrap_lines, bool do_wrap_breaks) const; + + String m_text_for_rendering; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp new file mode 100644 index 0000000000..6eeff109ea --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2018-2021, 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 <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/DOM/ParentNode.h> +#include <LibWeb/Dump.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> +#include <LibWeb/Layout/Node.h> +#include <LibWeb/Layout/TableBox.h> +#include <LibWeb/Layout/TableCellBox.h> +#include <LibWeb/Layout/TableRowBox.h> +#include <LibWeb/Layout/TextNode.h> +#include <LibWeb/Layout/TreeBuilder.h> + +namespace Web::Layout { + +TreeBuilder::TreeBuilder() +{ +} + +// The insertion_parent_for_*() functions maintain the invariant that block-level boxes must have either +// only block-level children or only inline-level children. + +static Layout::Node& insertion_parent_for_inline_node(Layout::NodeWithStyle& layout_parent) +{ + if (layout_parent.is_inline() && !layout_parent.is_inline_block()) + return layout_parent; + + if (!layout_parent.has_children() || layout_parent.children_are_inline()) + return layout_parent; + + // Parent has block-level children, insert into an anonymous wrapper block (and create it first if needed) + if (!layout_parent.last_child()->is_anonymous() || !layout_parent.last_child()->children_are_inline()) { + layout_parent.append_child(layout_parent.create_anonymous_wrapper()); + } + return *layout_parent.last_child(); +} + +static Layout::Node& insertion_parent_for_block_node(Layout::Node& layout_parent, Layout::Node& layout_node) +{ + if (!layout_parent.has_children()) { + // Parent block has no children, insert this block into parent. + return layout_parent; + } + + if (!layout_parent.children_are_inline()) { + // Parent block has block-level children, insert this block into parent. + return layout_parent; + } + + // Parent block has inline-level children (our siblings). + // First move these siblings into an anonymous wrapper block. + NonnullRefPtrVector<Layout::Node> children; + while (RefPtr<Layout::Node> child = layout_parent.first_child()) { + layout_parent.remove_child(*child); + children.append(child.release_nonnull()); + } + layout_parent.append_child(adopt(*new BlockBox(layout_node.document(), nullptr, layout_parent.computed_values().clone_inherited_values()))); + layout_parent.set_children_are_inline(false); + for (auto& child : children) { + layout_parent.last_child()->append_child(child); + } + layout_parent.last_child()->set_children_are_inline(true); + // Then it's safe to insert this block into parent. + return layout_parent; +} + +void TreeBuilder::create_layout_tree(DOM::Node& dom_node) +{ + // If the parent doesn't have a layout node, we don't need one either. + if (dom_node.parent() && !dom_node.parent()->layout_node()) + return; + + auto layout_node = dom_node.create_layout_node(); + if (!layout_node) + return; + + if (!dom_node.parent()) { + m_layout_root = layout_node; + } else { + if (layout_node->is_inline()) { + // Inlines can be inserted into the nearest ancestor. + auto& insertion_point = insertion_parent_for_inline_node(*m_parent_stack.last()); + insertion_point.append_child(*layout_node); + insertion_point.set_children_are_inline(true); + } else { + // Non-inlines can't be inserted into an inline parent, so find the nearest non-inline ancestor. + auto& nearest_non_inline_ancestor = [&]() -> Layout::Node& { + for (ssize_t i = m_parent_stack.size() - 1; i >= 0; --i) { + if (!m_parent_stack[i]->is_inline() || m_parent_stack[i]->is_inline_block()) + return *m_parent_stack[i]; + } + ASSERT_NOT_REACHED(); + }(); + auto& insertion_point = insertion_parent_for_block_node(nearest_non_inline_ancestor, *layout_node); + insertion_point.append_child(*layout_node); + insertion_point.set_children_are_inline(false); + } + } + + if (dom_node.has_children() && layout_node->can_have_children()) { + push_parent(downcast<NodeWithStyle>(*layout_node)); + downcast<DOM::ParentNode>(dom_node).for_each_child([&](auto& dom_child) { + create_layout_tree(dom_child); + }); + pop_parent(); + } +} + +RefPtr<Node> TreeBuilder::build(DOM::Node& dom_node) +{ + if (dom_node.parent()) { + // We're building a partial layout tree, so start by building up the stack of parent layout nodes. + for (auto* ancestor = dom_node.parent()->layout_node(); ancestor; ancestor = ancestor->parent()) + m_parent_stack.prepend(downcast<NodeWithStyle>(ancestor)); + } + + create_layout_tree(dom_node); + + if (auto* root = dom_node.document().layout_node()) + fixup_tables(*root); + + return move(m_layout_root); +} + +template<CSS::Display display, typename Callback> +void TreeBuilder::for_each_in_tree_with_display(NodeWithStyle& root, Callback callback) +{ + root.for_each_in_subtree_of_type<Box>([&](auto& box) { + if (box.computed_values().display() == display) + callback(box); + return IterationDecision::Continue; + }); +} + +void TreeBuilder::fixup_tables(NodeWithStyle& root) +{ + // NOTE: Even if we only do a partial build, we always do fixup from the root. + + remove_irrelevant_boxes(root); + generate_missing_child_wrappers(root); + generate_missing_parents(root); +} + +void TreeBuilder::remove_irrelevant_boxes(NodeWithStyle& root) +{ + // The following boxes are discarded as if they were display:none: + + NonnullRefPtrVector<Box> to_remove; + + // Children of a table-column. + for_each_in_tree_with_display<CSS::Display::TableColumn>(root, [&](Box& table_column) { + table_column.for_each_child([&](auto& child) { + to_remove.append(child); + }); + }); + + // Children of a table-column-group which are not a table-column. + for_each_in_tree_with_display<CSS::Display::TableColumnGroup>(root, [&](Box& table_column_group) { + table_column_group.for_each_child([&](auto& child) { + if (child.computed_values().display() != CSS::Display::TableColumn) + to_remove.append(child); + }); + }); + + // FIXME: + // Anonymous inline boxes which contain only white space and are between two immediate siblings each of which is a table-non-root box. + // Anonymous inline boxes which meet all of the following criteria: + // - they contain only white space + // - they are the first and/or last child of a tabular container + // - whose immediate sibling, if any, is a table-non-root box + + for (auto& box : to_remove) + box.parent()->remove_child(box); +} + +static bool is_table_track(CSS::Display display) +{ + return display == CSS::Display::TableRow || display == CSS::Display::TableColumn; +} + +static bool is_table_track_group(CSS::Display display) +{ + return display == CSS::Display::TableRowGroup || display == CSS::Display::TableColumnGroup; +} + +static bool is_not_proper_table_child(const Node& node) +{ + if (!node.has_style()) + return true; + auto display = node.computed_values().display(); + return !is_table_track_group(display) && !is_table_track(display) && display != CSS::Display::TableCaption; +} + +static bool is_not_table_row(const Node& node) +{ + if (!node.has_style()) + return true; + auto display = node.computed_values().display(); + return display != CSS::Display::TableRow; +} + +static bool is_not_table_cell(const Node& node) +{ + if (!node.has_style()) + return true; + auto display = node.computed_values().display(); + return display != CSS::Display::TableCell; +} + +template<typename Matcher, typename Callback> +static void for_each_sequence_of_consecutive_children_matching(NodeWithStyle& parent, Matcher matcher, Callback callback) +{ + NonnullRefPtrVector<Node> sequence; + Node* next_sibling = nullptr; + for (auto* child = parent.first_child(); child; child = next_sibling) { + next_sibling = child->next_sibling(); + if (matcher(*child)) { + sequence.append(*child); + } else { + if (!sequence.is_empty()) { + callback(sequence, next_sibling); + sequence.clear(); + } + } + } + if (!sequence.is_empty()) + callback(sequence, nullptr); +} + +template<typename WrapperBoxType> +static void wrap_in_anonymous(NonnullRefPtrVector<Node>& sequence, Node* nearest_sibling) +{ + ASSERT(!sequence.is_empty()); + auto& parent = *sequence.first().parent(); + auto computed_values = parent.computed_values().clone_inherited_values(); + static_cast<CSS::MutableComputedValues&>(computed_values).set_display(WrapperBoxType::static_display()); + auto wrapper = adopt(*new WrapperBoxType(parent.document(), nullptr, move(computed_values))); + for (auto& child : sequence) { + parent.remove_child(child); + wrapper->append_child(child); + } + if (nearest_sibling) + parent.insert_before(move(wrapper), *nearest_sibling); + else + parent.append_child(move(wrapper)); +} + +void TreeBuilder::generate_missing_child_wrappers(NodeWithStyle& root) +{ + // An anonymous table-row box must be generated around each sequence of consecutive children of a table-root box which are not proper table child boxes. + for_each_in_tree_with_display<CSS::Display::Table>(root, [&](auto& parent) { + for_each_sequence_of_consecutive_children_matching(parent, is_not_proper_table_child, [&](auto sequence, auto nearest_sibling) { + wrap_in_anonymous<TableRowBox>(sequence, nearest_sibling); + }); + }); + + // An anonymous table-row box must be generated around each sequence of consecutive children of a table-row-group box which are not table-row boxes. + for_each_in_tree_with_display<CSS::Display::TableRowGroup>(root, [&](auto& parent) { + for_each_sequence_of_consecutive_children_matching(parent, is_not_table_row, [&](auto& sequence, auto nearest_sibling) { + wrap_in_anonymous<TableRowBox>(sequence, nearest_sibling); + }); + }); + + // An anonymous table-cell box must be generated around each sequence of consecutive children of a table-row box which are not table-cell boxes. !Testcase + for_each_in_tree_with_display<CSS::Display::TableRow>(root, [&](auto& parent) { + for_each_sequence_of_consecutive_children_matching(parent, is_not_table_cell, [&](auto& sequence, auto nearest_sibling) { + wrap_in_anonymous<TableCellBox>(sequence, nearest_sibling); + }); + }); +} + +void TreeBuilder::generate_missing_parents(NodeWithStyle&) +{ + // FIXME: Implement. +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.h b/Userland/Libraries/LibWeb/Layout/TreeBuilder.h new file mode 100644 index 0000000000..03a87cc287 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018-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 <AK/NonnullRefPtrVector.h> +#include <AK/RefPtr.h> +#include <LibWeb/Forward.h> + +namespace Web::Layout { + +class TreeBuilder { +public: + TreeBuilder(); + + RefPtr<Layout::Node> build(DOM::Node&); + +private: + void create_layout_tree(DOM::Node&); + + void push_parent(Layout::NodeWithStyle& node) { m_parent_stack.append(&node); } + void pop_parent() { m_parent_stack.take_last(); } + + template<CSS::Display, typename Callback> + void for_each_in_tree_with_display(NodeWithStyle& root, Callback); + + void fixup_tables(NodeWithStyle& root); + void remove_irrelevant_boxes(NodeWithStyle& root); + void generate_missing_child_wrappers(NodeWithStyle& root); + void generate_missing_parents(NodeWithStyle& root); + + RefPtr<Layout::Node> m_layout_root; + Vector<Layout::NodeWithStyle*> m_parent_stack; +}; + +} diff --git a/Userland/Libraries/LibWeb/Layout/WidgetBox.cpp b/Userland/Libraries/LibWeb/Layout/WidgetBox.cpp new file mode 100644 index 0000000000..c38aa50765 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/WidgetBox.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018-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 <LibGUI/Painter.h> +#include <LibGUI/ScrollBar.h> +#include <LibGUI/Widget.h> +#include <LibGfx/Font.h> +#include <LibGfx/StylePainter.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/InProcessWebView.h> +#include <LibWeb/Layout/WidgetBox.h> +#include <LibWeb/Page/Frame.h> + +namespace Web::Layout { + +WidgetBox::WidgetBox(DOM::Document& document, DOM::Element& element, GUI::Widget& widget) + : ReplacedBox(document, element, CSS::StyleProperties::create()) + , m_widget(widget) +{ + set_has_intrinsic_width(true); + set_has_intrinsic_height(true); + set_intrinsic_width(widget.width()); + set_intrinsic_height(widget.height()); +} + +WidgetBox::~WidgetBox() +{ + widget().remove_from_parent(); +} + +void WidgetBox::did_set_rect() +{ + ReplacedBox::did_set_rect(); + update_widget(); +} + +void WidgetBox::update_widget() +{ + auto adjusted_widget_position = absolute_rect().location().to_type<int>(); + auto& page_view = static_cast<const InProcessWebView&>(frame().page()->client()); + adjusted_widget_position.move_by(-page_view.horizontal_scrollbar().value(), -page_view.vertical_scrollbar().value()); + widget().move_to(adjusted_widget_position); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/WidgetBox.h b/Userland/Libraries/LibWeb/Layout/WidgetBox.h new file mode 100644 index 0000000000..8c7445a1fc --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/WidgetBox.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-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/Layout/ReplacedBox.h> + +namespace Web::Layout { + +class WidgetBox final : public ReplacedBox { +public: + WidgetBox(DOM::Document&, DOM::Element&, GUI::Widget&); + virtual ~WidgetBox() override; + + GUI::Widget& widget() { return m_widget; } + const GUI::Widget& widget() const { return m_widget; } + + void update_widget(); + +private: + virtual void did_set_rect() override; + + NonnullRefPtr<GUI::Widget> m_widget; +}; + +} diff --git a/Userland/Libraries/LibWeb/LayoutTreeModel.cpp b/Userland/Libraries/LibWeb/LayoutTreeModel.cpp new file mode 100644 index 0000000000..440b2fce52 --- /dev/null +++ b/Userland/Libraries/LibWeb/LayoutTreeModel.cpp @@ -0,0 +1,163 @@ +/* + * 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 "LayoutTreeModel.h" +#include <AK/StringBuilder.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/DOM/Text.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> +#include <LibWeb/Layout/TextNode.h> +#include <ctype.h> +#include <stdio.h> + +namespace Web { + +LayoutTreeModel::LayoutTreeModel(DOM::Document& document) + : m_document(document) +{ + m_document_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-html.png")); + m_element_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object.png")); + m_text_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-unknown.png")); +} + +LayoutTreeModel::~LayoutTreeModel() +{ +} + +GUI::ModelIndex LayoutTreeModel::index(int row, int column, const GUI::ModelIndex& parent) const +{ + if (!parent.is_valid()) + return create_index(row, column, m_document->layout_node()); + auto& parent_node = *static_cast<Layout::Node*>(parent.internal_data()); + return create_index(row, column, parent_node.child_at_index(row)); +} + +GUI::ModelIndex LayoutTreeModel::parent_index(const GUI::ModelIndex& index) const +{ + if (!index.is_valid()) + return {}; + auto& node = *static_cast<Layout::Node*>(index.internal_data()); + if (!node.parent()) + return {}; + + // No grandparent? Parent is the document! + if (!node.parent()->parent()) { + return create_index(0, 0, m_document->layout_node()); + } + + // Walk the grandparent's children to find the index of node's parent in its parent. + // (This is needed to produce the row number of the GUI::ModelIndex corresponding to node's parent.) + int grandparent_child_index = 0; + for (auto* grandparent_child = node.parent()->parent()->first_child(); grandparent_child; grandparent_child = grandparent_child->next_sibling()) { + if (grandparent_child == node.parent()) + return create_index(grandparent_child_index, 0, node.parent()); + ++grandparent_child_index; + } + + ASSERT_NOT_REACHED(); + return {}; +} + +int LayoutTreeModel::row_count(const GUI::ModelIndex& index) const +{ + if (!index.is_valid()) + return 1; + auto& node = *static_cast<Layout::Node*>(index.internal_data()); + return node.child_count(); +} + +int LayoutTreeModel::column_count(const GUI::ModelIndex&) const +{ + return 1; +} + +static String with_whitespace_collapsed(const StringView& string) +{ + StringBuilder builder; + for (size_t i = 0; i < string.length(); ++i) { + if (isspace(string[i])) { + builder.append(' '); + while (i < string.length()) { + if (isspace(string[i])) { + ++i; + continue; + } + builder.append(string[i]); + break; + } + continue; + } + builder.append(string[i]); + } + return builder.to_string(); +} + +GUI::Variant LayoutTreeModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const +{ + auto& node = *static_cast<Layout::Node*>(index.internal_data()); + if (role == GUI::ModelRole::Icon) { + if (is<Layout::InitialContainingBlockBox>(node)) + return m_document_icon; + if (is<Layout::TextNode>(node)) + return m_text_icon; + return m_element_icon; + } + if (role == GUI::ModelRole::Display) { + if (is<Layout::TextNode>(node)) + return String::formatted("TextNode: {}", with_whitespace_collapsed(downcast<Layout::TextNode>(node).text_for_rendering())); + StringBuilder builder; + builder.append(node.class_name()); + builder.append(' '); + if (node.is_anonymous()) { + builder.append("[anonymous]"); + } else if (!node.dom_node()->is_element()) { + builder.append(node.dom_node()->node_name()); + } else { + auto& element = downcast<DOM::Element>(*node.dom_node()); + builder.append('<'); + builder.append(element.local_name()); + element.for_each_attribute([&](auto& name, auto& value) { + builder.append(' '); + builder.append(name); + builder.append('='); + builder.append('"'); + builder.append(value); + builder.append('"'); + }); + builder.append('>'); + } + return builder.to_string(); + } + return {}; +} + +void LayoutTreeModel::update() +{ + did_update(); +} + +} diff --git a/Userland/Libraries/LibWeb/LayoutTreeModel.h b/Userland/Libraries/LibWeb/LayoutTreeModel.h new file mode 100644 index 0000000000..f1deedcce1 --- /dev/null +++ b/Userland/Libraries/LibWeb/LayoutTreeModel.h @@ -0,0 +1,60 @@ +/* + * 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 <LibGUI/Model.h> +#include <LibWeb/Forward.h> + +namespace Web { + +class LayoutTreeModel final : public GUI::Model { +public: + static NonnullRefPtr<LayoutTreeModel> create(DOM::Document& document) + { + return adopt(*new LayoutTreeModel(document)); + } + + virtual ~LayoutTreeModel() override; + + virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; + virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; + virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; + virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override; + virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override; + virtual void update() override; + +private: + explicit LayoutTreeModel(DOM::Document&); + + NonnullRefPtr<DOM::Document> m_document; + + GUI::Icon m_document_icon; + GUI::Icon m_element_icon; + GUI::Icon m_text_icon; +}; + +} diff --git a/Userland/Libraries/LibWeb/Loader/ContentFilter.cpp b/Userland/Libraries/LibWeb/Loader/ContentFilter.cpp new file mode 100644 index 0000000000..b4633b5d01 --- /dev/null +++ b/Userland/Libraries/LibWeb/Loader/ContentFilter.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021, 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/StringBuilder.h> +#include <LibWeb/Loader/ContentFilter.h> + +namespace Web { + +ContentFilter& ContentFilter::the() +{ + static ContentFilter* filter = new ContentFilter; + return *filter; +} + +ContentFilter::ContentFilter() +{ +} + +ContentFilter::~ContentFilter() +{ +} + +bool ContentFilter::is_filtered(const URL& url) const +{ + auto url_string = url.to_string(); + + for (auto& pattern : m_patterns) { + if (url_string.matches(pattern.text, CaseSensitivity::CaseSensitive)) + return true; + } + return false; +} + +void ContentFilter::add_pattern(const String& pattern) +{ + StringBuilder builder; + if (!pattern.starts_with('*')) + builder.append('*'); + builder.append(pattern); + if (!pattern.ends_with('*')) + builder.append('*'); + m_patterns.empend(builder.to_string()); +} + +} diff --git a/Userland/Libraries/LibWeb/Loader/ContentFilter.h b/Userland/Libraries/LibWeb/Loader/ContentFilter.h new file mode 100644 index 0000000000..964c3b3fa2 --- /dev/null +++ b/Userland/Libraries/LibWeb/Loader/ContentFilter.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021, 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 <AK/URL.h> +#include <AK/Vector.h> + +namespace Web { + +class ContentFilter { +public: + static ContentFilter& the(); + + bool is_filtered(const URL&) const; + void add_pattern(const String&); + +private: + ContentFilter(); + ~ContentFilter(); + + struct Pattern { + String text; + }; + Vector<Pattern> m_patterns; +}; + +} diff --git a/Userland/Libraries/LibWeb/Loader/FrameLoader.cpp b/Userland/Libraries/LibWeb/Loader/FrameLoader.cpp new file mode 100644 index 0000000000..172a9e1b2a --- /dev/null +++ b/Userland/Libraries/LibWeb/Loader/FrameLoader.cpp @@ -0,0 +1,296 @@ +/* + * 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/LexicalPath.h> +#include <LibGemini/Document.h> +#include <LibGfx/ImageDecoder.h> +#include <LibMarkdown/Document.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/ElementFactory.h> +#include <LibWeb/DOM/Text.h> +#include <LibWeb/HTML/HTMLIFrameElement.h> +#include <LibWeb/HTML/Parser/HTMLDocumentParser.h> +#include <LibWeb/Loader/FrameLoader.h> +#include <LibWeb/Loader/ResourceLoader.h> +#include <LibWeb/Namespace.h> +#include <LibWeb/Page/Frame.h> +#include <LibWeb/Page/Page.h> + +//#define GEMINI_DEBUG 1 + +namespace Web { + +FrameLoader::FrameLoader(Frame& frame) + : m_frame(frame) +{ +} + +FrameLoader::~FrameLoader() +{ +} + +static bool build_markdown_document(DOM::Document& document, const ByteBuffer& data) +{ + auto markdown_document = Markdown::Document::parse(data); + if (!markdown_document) + return false; + + HTML::HTMLDocumentParser parser(document, markdown_document->render_to_html(), "utf-8"); + parser.run(document.url()); + return true; +} + +static bool build_text_document(DOM::Document& document, const ByteBuffer& data) +{ + auto html_element = document.create_element("html"); + document.append_child(html_element); + + auto head_element = document.create_element("head"); + html_element->append_child(head_element); + auto title_element = document.create_element("title"); + 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"); + html_element->append_child(body_element); + + auto pre_element = document.create_element("pre"); + body_element->append_child(pre_element); + + pre_element->append_child(document.create_text_node(String::copy(data))); + return true; +} + +static bool build_image_document(DOM::Document& document, const ByteBuffer& data) +{ + auto image_decoder = Gfx::ImageDecoder::create(data.data(), data.size()); + auto bitmap = image_decoder->bitmap(); + if (!bitmap) + return false; + + auto html_element = document.create_element("html"); + document.append_child(html_element); + + auto head_element = document.create_element("head"); + html_element->append_child(head_element); + auto title_element = document.create_element("title"); + head_element->append_child(title_element); + + auto basename = LexicalPath(document.url().path()).basename(); + auto title_text = adopt(*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"); + html_element->append_child(body_element); + + auto image_element = document.create_element("img"); + image_element->set_attribute(HTML::AttributeNames::src, document.url().to_string()); + body_element->append_child(image_element); + + return true; +} + +static bool build_gemini_document(DOM::Document& document, const ByteBuffer& data) +{ + StringView gemini_data { data }; + auto gemini_document = Gemini::Document::parse(gemini_data, document.url()); + String html_data = gemini_document->render_to_html(); + +#ifdef GEMINI_DEBUG + dbgln("Gemini data:\n\"\"\"{}\"\"\"", gemini_data); + dbgln("Converted to HTML:\n\"\"\"{}\"\"\"", html_data); +#endif + + HTML::HTMLDocumentParser parser(document, html_data, "utf-8"); + parser.run(document.url()); + return true; +} + +bool FrameLoader::parse_document(DOM::Document& document, const ByteBuffer& data) +{ + auto& mime_type = document.content_type(); + if (mime_type == "text/html" || mime_type == "image/svg+xml") { + HTML::HTMLDocumentParser parser(document, data, document.encoding()); + parser.run(document.url()); + return true; + } + if (mime_type.starts_with("image/")) + return build_image_document(document, data); + if (mime_type == "text/plain") + return build_text_document(document, data); + if (mime_type == "text/markdown") + return build_markdown_document(document, data); + if (mime_type == "text/gemini") + return build_gemini_document(document, data); + + return false; +} + +bool FrameLoader::load(const LoadRequest& request, Type type) +{ + if (!request.is_valid()) { + load_error_page(request.url(), "Invalid request"); + return false; + } + + auto& url = request.url(); + + set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, request)); + + if (type == Type::Navigation) { + if (auto* page = frame().page()) + page->client().page_did_start_loading(url); + } + + if (type == Type::IFrame) + return true; + + if (url.protocol() == "http" || url.protocol() == "https") { + URL favicon_url; + favicon_url.set_protocol(url.protocol()); + favicon_url.set_host(url.host()); + favicon_url.set_port(url.port()); + favicon_url.set_path("/favicon.ico"); + + ResourceLoader::the().load( + favicon_url, + [this, favicon_url](auto data, auto&) { + dbg() << "Favicon downloaded, " << data.size() << " bytes from " << favicon_url; + auto decoder = Gfx::ImageDecoder::create(data.data(), data.size()); + auto bitmap = decoder->bitmap(); + if (!bitmap) { + dbg() << "Could not decode favicon " << favicon_url; + return; + } + dbg() << "Decoded favicon, " << bitmap->size(); + if (auto* page = frame().page()) + page->client().page_did_change_favicon(*bitmap); + }); + } + + return true; +} + +bool FrameLoader::load(const URL& url, Type type) +{ + dbg() << "FrameLoader::load: " << url; + + if (!url.is_valid()) { + load_error_page(url, "Invalid URL"); + return false; + } + + LoadRequest request; + request.set_url(url); + + return load(request, type); +} + +void FrameLoader::load_html(const StringView& html, const URL& url) +{ + auto document = DOM::Document::create(url); + HTML::HTMLDocumentParser parser(document, html, "utf-8"); + parser.run(url); + frame().set_document(&parser.document()); +} + +// FIXME: Use an actual templating engine (our own one when it's built, preferably +// with a way to check these usages at compile time) + +void FrameLoader::load_error_page(const URL& failed_url, const String& error) +{ + auto error_page_url = "file:///res/html/error.html"; + ResourceLoader::the().load( + error_page_url, + [this, failed_url, error](auto data, auto&) { + ASSERT(!data.is_null()); +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + auto html = String::format( + String::copy(data).characters(), + escape_html_entities(failed_url.to_string()).characters(), + escape_html_entities(error).characters()); +#pragma GCC diagnostic pop + auto document = HTML::parse_html_document(html, failed_url, "utf-8"); + ASSERT(document); + frame().set_document(document); + }, + [](auto error) { + dbg() << "Failed to load error page: " << error; + ASSERT_NOT_REACHED(); + }); +} + +void FrameLoader::resource_did_load() +{ + auto url = resource()->url(); + + if (!resource()->has_encoded_data()) { + load_error_page(url, "No data"); + return; + } + + // FIXME: Also check HTTP status code before redirecting + auto location = resource()->response_headers().get("Location"); + if (location.has_value()) { + load(url.complete_url(location.value()), FrameLoader::Type::Navigation); + return; + } + + dbgln("I believe this content has MIME type '{}', , encoding '{}'", resource()->mime_type(), resource()->encoding()); + + auto document = DOM::Document::create(); + document->set_url(url); + document->set_encoding(resource()->encoding()); + document->set_content_type(resource()->mime_type()); + + frame().set_document(document); + + if (!parse_document(*document, resource()->encoded_data())) { + load_error_page(url, "Failed to parse content."); + return; + } + + if (!url.fragment().is_empty()) + frame().scroll_to_anchor(url.fragment()); + + if (auto* host_element = frame().host_element()) { + // FIXME: Perhaps in the future we'll have a better common base class for <frame> and <iframe> + ASSERT(is<HTML::HTMLIFrameElement>(*host_element)); + downcast<HTML::HTMLIFrameElement>(*host_element).content_frame_did_load({}); + } + + if (auto* page = frame().page()) + page->client().page_did_finish_loading(url); +} + +void FrameLoader::resource_did_fail() +{ + load_error_page(resource()->url(), resource()->error()); +} + +} diff --git a/Userland/Libraries/LibWeb/Loader/FrameLoader.h b/Userland/Libraries/LibWeb/Loader/FrameLoader.h new file mode 100644 index 0000000000..f0c3d0bf56 --- /dev/null +++ b/Userland/Libraries/LibWeb/Loader/FrameLoader.h @@ -0,0 +1,66 @@ +/* + * 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 <AK/Forward.h> +#include <LibWeb/Forward.h> +#include <LibWeb/Loader/Resource.h> + +namespace Web { + +class FrameLoader final + : public ResourceClient { +public: + enum class Type { + Navigation, + Reload, + IFrame, + }; + + explicit FrameLoader(Frame&); + ~FrameLoader(); + + bool load(const URL&, Type); + bool load(const LoadRequest&, Type); + + void load_html(const StringView&, const URL&); + + Frame& frame() { return m_frame; } + const Frame& frame() const { return m_frame; } + +private: + // ^ResourceClient + virtual void resource_did_load() override; + virtual void resource_did_fail() override; + + void load_error_page(const URL& failed_url, const String& error_message); + bool parse_document(DOM::Document&, const ByteBuffer& data); + + Frame& m_frame; +}; + +} diff --git a/Userland/Libraries/LibWeb/Loader/ImageLoader.cpp b/Userland/Libraries/LibWeb/Loader/ImageLoader.cpp new file mode 100644 index 0000000000..b56ab98abd --- /dev/null +++ b/Userland/Libraries/LibWeb/Loader/ImageLoader.cpp @@ -0,0 +1,164 @@ +/* + * 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 <LibCore/Timer.h> +#include <LibGfx/Bitmap.h> +#include <LibGfx/ImageDecoder.h> +#include <LibWeb/Loader/ImageLoader.h> +#include <LibWeb/Loader/ResourceLoader.h> + +namespace Web { + +ImageLoader::ImageLoader() + : m_timer(Core::Timer::construct()) +{ +} + +void ImageLoader::load(const URL& url) +{ + m_loading_state = LoadingState::Loading; + LoadRequest request; + request.set_url(url); + set_resource(ResourceLoader::the().load_resource(Resource::Type::Image, request)); +} + +void ImageLoader::set_visible_in_viewport(bool visible_in_viewport) const +{ + if (m_visible_in_viewport == visible_in_viewport) + return; + m_visible_in_viewport = visible_in_viewport; + + // FIXME: Don't update volatility every time. If we're here, we're probably scanning through + // the whole document, updating "is visible in viewport" flags, and this could lead + // to the same bitmap being marked volatile back and forth unnecessarily. + if (resource()) + const_cast<ImageResource*>(resource())->update_volatility(); +} + +void ImageLoader::resource_did_load() +{ + ASSERT(resource()); + + if (!resource()->mime_type().starts_with("image/")) { + m_loading_state = LoadingState::Failed; + if (on_fail) + on_fail(); + return; + } + + m_loading_state = LoadingState::Loaded; + +#ifdef IMAGE_LOADER_DEBUG + if (!resource()->has_encoded_data()) { + dbg() << "ImageLoader: Resource did load, no encoded data. URL: " << resource()->url(); + } else { + dbg() << "ImageLoader: Resource did load, has encoded data. URL: " << resource()->url(); + } +#endif + + if (resource()->should_decode_in_process()) { + auto& decoder = resource()->ensure_decoder(); + + if (decoder.is_animated() && decoder.frame_count() > 1) { + const auto& first_frame = decoder.frame(0); + m_timer->set_interval(first_frame.duration); + m_timer->on_timeout = [this] { animate(); }; + m_timer->start(); + } + } + + if (on_load) + on_load(); +} + +void ImageLoader::animate() +{ + if (!m_visible_in_viewport) + return; + + auto& decoder = resource()->ensure_decoder(); + + m_current_frame_index = (m_current_frame_index + 1) % decoder.frame_count(); + const auto& current_frame = decoder.frame(m_current_frame_index); + + if (current_frame.duration != m_timer->interval()) { + m_timer->restart(current_frame.duration); + } + + if (m_current_frame_index == decoder.frame_count() - 1) { + ++m_loops_completed; + if (m_loops_completed > 0 && m_loops_completed == decoder.loop_count()) { + m_timer->stop(); + } + } + + if (on_animate) + on_animate(); +} + +void ImageLoader::resource_did_fail() +{ + dbg() << "ImageLoader: Resource did fail. URL: " << resource()->url(); + m_loading_state = LoadingState::Failed; + if (on_fail) + on_fail(); +} + +bool ImageLoader::has_image() const +{ + if (!resource()) + return false; + if (resource()->should_decode_in_process()) + return const_cast<ImageResource*>(resource())->ensure_decoder().bitmap(); + return true; +} + +unsigned ImageLoader::width() const +{ + if (!resource()) + return 0; + if (resource()->should_decode_in_process()) + return const_cast<ImageResource*>(resource())->ensure_decoder().width(); + return bitmap() ? bitmap()->width() : 0; +} + +unsigned ImageLoader::height() const +{ + if (!resource()) + return 0; + if (resource()->should_decode_in_process()) + return const_cast<ImageResource*>(resource())->ensure_decoder().height(); + return bitmap() ? bitmap()->height() : 0; +} + +const Gfx::Bitmap* ImageLoader::bitmap() const +{ + if (!resource()) + return nullptr; + return resource()->bitmap(m_current_frame_index); +} + +} diff --git a/Userland/Libraries/LibWeb/Loader/ImageLoader.h b/Userland/Libraries/LibWeb/Loader/ImageLoader.h new file mode 100644 index 0000000000..f07b6e3055 --- /dev/null +++ b/Userland/Libraries/LibWeb/Loader/ImageLoader.h @@ -0,0 +1,79 @@ +/* + * 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 <AK/Function.h> +#include <LibCore/Timer.h> +#include <LibWeb/Loader/ImageResource.h> + +namespace Web { + +class ImageLoader : public ImageResourceClient { +public: + ImageLoader(); + + void load(const URL&); + + const Gfx::Bitmap* bitmap() const; + + bool has_image() const; + + bool has_loaded_or_failed() const { return m_loading_state != LoadingState::Loading; } + + void set_visible_in_viewport(bool) const; + + unsigned width() const; + unsigned height() const; + + Function<void()> on_load; + Function<void()> on_fail; + Function<void()> on_animate; + +private: + // ^ImageResourceClient + virtual void resource_did_load() override; + virtual void resource_did_fail() override; + virtual bool is_visible_in_viewport() const override { return m_visible_in_viewport; } + + void animate(); + + enum class LoadingState { + None, + Loading, + Loaded, + Failed, + }; + + mutable bool m_visible_in_viewport { false }; + + size_t m_current_frame_index { 0 }; + size_t m_loops_completed { 0 }; + LoadingState m_loading_state { LoadingState::Loading }; + NonnullRefPtr<Core::Timer> m_timer; +}; + +} diff --git a/Userland/Libraries/LibWeb/Loader/ImageResource.cpp b/Userland/Libraries/LibWeb/Loader/ImageResource.cpp new file mode 100644 index 0000000000..3daa284deb --- /dev/null +++ b/Userland/Libraries/LibWeb/Loader/ImageResource.cpp @@ -0,0 +1,103 @@ +/* + * 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/Function.h> +#include <LibGfx/Bitmap.h> +#include <LibGfx/ImageDecoder.h> +#include <LibImageDecoderClient/Client.h> +#include <LibWeb/Loader/ImageResource.h> + +namespace Web { + +ImageResource::ImageResource(const LoadRequest& request) + : Resource(Type::Image, request) +{ +} + +ImageResource::~ImageResource() +{ +} + +bool ImageResource::should_decode_in_process() const +{ + return mime_type() == "image/gif"; +} + +Gfx::ImageDecoder& ImageResource::ensure_decoder() +{ + if (!m_decoder) + m_decoder = Gfx::ImageDecoder::create(encoded_data()); + return *m_decoder; +} + +const Gfx::Bitmap* ImageResource::bitmap(size_t frame_index) const +{ + if (!has_encoded_data()) + return nullptr; + + if (should_decode_in_process()) { + if (!m_decoder) + return nullptr; + if (m_decoder->is_animated()) + m_decoded_image = m_decoder->frame(frame_index).image; + else + m_decoded_image = m_decoder->bitmap(); + } else if (!m_decoded_image && !m_has_attempted_decode) { + auto image_decoder_client = ImageDecoderClient::Client::construct(); + m_decoded_image = image_decoder_client->decode_image(encoded_data()); + m_has_attempted_decode = true; + } + return m_decoded_image; +} + +void ImageResource::update_volatility() +{ + if (!m_decoder) + return; + + bool visible_in_viewport = false; + for_each_client([&](auto& client) { + if (static_cast<const ImageResourceClient&>(client).is_visible_in_viewport()) + visible_in_viewport = true; + }); + + if (!visible_in_viewport) { + m_decoder->set_volatile(); + return; + } + + bool still_has_decoded_image = m_decoder->set_nonvolatile(); + if (still_has_decoded_image) + return; + + m_decoder = nullptr; +} + +ImageResourceClient::~ImageResourceClient() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/Loader/ImageResource.h b/Userland/Libraries/LibWeb/Loader/ImageResource.h new file mode 100644 index 0000000000..0e66785a70 --- /dev/null +++ b/Userland/Libraries/LibWeb/Loader/ImageResource.h @@ -0,0 +1,66 @@ +/* + * 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/Loader/Resource.h> + +namespace Web { + +class ImageResource final : public Resource { + friend class Resource; + +public: + virtual ~ImageResource() override; + Gfx::ImageDecoder& ensure_decoder(); + const Gfx::Bitmap* bitmap(size_t frame_index = 0) const; + + bool should_decode_in_process() const; + + void update_volatility(); + +private: + explicit ImageResource(const LoadRequest&); + RefPtr<Gfx::ImageDecoder> m_decoder; + mutable RefPtr<Gfx::Bitmap> m_decoded_image; + mutable bool m_has_attempted_decode { false }; +}; + +class ImageResourceClient : public ResourceClient { +public: + virtual ~ImageResourceClient(); + + virtual bool is_visible_in_viewport() const { return false; } + +protected: + ImageResource* resource() { return static_cast<ImageResource*>(ResourceClient::resource()); } + const ImageResource* resource() const { return static_cast<const ImageResource*>(ResourceClient::resource()); } + +private: + virtual Resource::Type client_type() const override { return Resource::Type::Image; } +}; + +} diff --git a/Userland/Libraries/LibWeb/Loader/LoadRequest.h b/Userland/Libraries/LibWeb/Loader/LoadRequest.h new file mode 100644 index 0000000000..149ce2537c --- /dev/null +++ b/Userland/Libraries/LibWeb/Loader/LoadRequest.h @@ -0,0 +1,94 @@ +/* + * 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 <AK/ByteBuffer.h> +#include <AK/HashMap.h> +#include <AK/URL.h> +#include <LibWeb/Forward.h> + +namespace Web { + +class LoadRequest { +public: + LoadRequest() + { + } + + bool is_valid() const { return m_url.is_valid(); } + + const URL& url() const { return m_url; } + void set_url(const URL& url) { m_url = url; } + + const String& method() const { return m_method; } + void set_method(const String& method) { m_method = method; } + + const ByteBuffer& body() const { return m_body; } + void set_body(const ByteBuffer& body) { m_body = body; } + + unsigned hash() const + { + // FIXME: Include headers in the hash as well + return pair_int_hash(pair_int_hash(m_url.to_string().hash(), m_method.hash()), string_hash((const char*)m_body.data(), m_body.size())); + } + + bool operator==(const LoadRequest& other) const + { + if (m_headers.size() != other.m_headers.size()) + return false; + for (auto& it : m_headers) { + auto jt = other.m_headers.find(it.key); + if (jt == other.m_headers.end()) + return false; + if (it.value != jt->value) + return false; + } + return m_url == other.m_url && m_method == other.m_method && m_body == other.m_body; + } + + void set_header(const String& name, const String& value) { m_headers.set(name, value); } + String header(const String& name) const { return m_headers.get(name).value_or({}); } + + const HashMap<String, String>& headers() const { return m_headers; } + +private: + URL m_url; + String m_method { "GET" }; + HashMap<String, String> m_headers; + ByteBuffer m_body; +}; + +} + +namespace AK { + +template<> +struct Traits<Web::LoadRequest> : public GenericTraits<Web::LoadRequest> { + static unsigned hash(const Web::LoadRequest& request) { return request.hash(); } +}; + +} diff --git a/Userland/Libraries/LibWeb/Loader/Resource.cpp b/Userland/Libraries/LibWeb/Loader/Resource.cpp new file mode 100644 index 0000000000..2cb6ebc52d --- /dev/null +++ b/Userland/Libraries/LibWeb/Loader/Resource.cpp @@ -0,0 +1,168 @@ +/* + * 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/Function.h> +#include <LibCore/MimeData.h> +#include <LibWeb/HTML/HTMLImageElement.h> +#include <LibWeb/Loader/Resource.h> + +namespace Web { + +NonnullRefPtr<Resource> Resource::create(Badge<ResourceLoader>, Type type, const LoadRequest& request) +{ + if (type == Type::Image) + return adopt(*new ImageResource(request)); + return adopt(*new Resource(type, request)); +} + +Resource::Resource(Type type, const LoadRequest& request) + : m_request(request) + , m_type(type) +{ +} + +Resource::~Resource() +{ +} + +void Resource::for_each_client(Function<void(ResourceClient&)> callback) +{ + Vector<WeakPtr<ResourceClient>, 16> clients_copy; + clients_copy.ensure_capacity(m_clients.size()); + for (auto* client : m_clients) + clients_copy.append(client->make_weak_ptr()); + for (auto client : clients_copy) { + if (client) + callback(*client); + } +} + +static String encoding_from_content_type(const String& content_type) +{ + auto offset = content_type.index_of("charset="); + if (offset.has_value()) { + auto encoding = content_type.substring(offset.value() + 8, content_type.length() - offset.value() - 8).to_lowercase(); + if (encoding.length() >= 2 && encoding.starts_with('"') && encoding.ends_with('"')) + return encoding.substring(1, encoding.length() - 2); + if (encoding.length() >= 2 && encoding.starts_with('\'') && encoding.ends_with('\'')) + return encoding.substring(1, encoding.length() - 2); + return encoding; + } + + return "utf-8"; +} + +static String mime_type_from_content_type(const String& content_type) +{ + auto offset = content_type.index_of(";"); + if (offset.has_value()) + return content_type.substring(0, offset.value()).to_lowercase(); + + return content_type; +} + +void Resource::did_load(Badge<ResourceLoader>, ReadonlyBytes data, const HashMap<String, String, CaseInsensitiveStringTraits>& headers) +{ + ASSERT(!m_loaded); + m_encoded_data = ByteBuffer::copy(data); + m_response_headers = headers; + m_loaded = true; + + auto content_type = headers.get("Content-Type"); + if (content_type.has_value()) { +#ifdef RESOURCE_DEBUG + dbgln("Content-Type header: '{}'", content_type.value()); +#endif + m_encoding = encoding_from_content_type(content_type.value()); + m_mime_type = mime_type_from_content_type(content_type.value()); + } else if (url().protocol() == "data" && !url().data_mime_type().is_empty()) { +#ifdef RESOURCE_DEBUG + dbg() << "This is a data URL with mime-type _" << url().data_mime_type() << "_"; +#endif + m_encoding = "utf-8"; // FIXME: This doesn't seem nice. + m_mime_type = url().data_mime_type(); + } else { +#ifdef RESOURCE_DEBUG + dbgln("No Content-Type header to go on! Guessing based on filename..."); +#endif + m_encoding = "utf-8"; // FIXME: This doesn't seem nice. + m_mime_type = Core::guess_mime_type_based_on_filename(url().path()); + } + + for_each_client([](auto& client) { + client.resource_did_load(); + }); +} + +void Resource::did_fail(Badge<ResourceLoader>, const String& error) +{ + m_error = error; + m_failed = true; + + for_each_client([](auto& client) { + client.resource_did_fail(); + }); +} + +void Resource::register_client(Badge<ResourceClient>, ResourceClient& client) +{ + ASSERT(!m_clients.contains(&client)); + m_clients.set(&client); +} + +void Resource::unregister_client(Badge<ResourceClient>, ResourceClient& client) +{ + ASSERT(m_clients.contains(&client)); + m_clients.remove(&client); +} + +void ResourceClient::set_resource(Resource* resource) +{ + if (m_resource) + m_resource->unregister_client({}, *this); + m_resource = resource; + if (m_resource) { + ASSERT(resource->type() == client_type()); + + m_resource->register_client({}, *this); + + // Make sure that reused resources also have their load callback fired. + if (resource->is_loaded()) + resource_did_load(); + + // Make sure that reused resources also have their fail callback fired. + if (resource->is_failed()) + resource_did_fail(); + } +} + +ResourceClient::~ResourceClient() +{ + if (m_resource) + m_resource->unregister_client({}, *this); +} + +} diff --git a/Userland/Libraries/LibWeb/Loader/Resource.h b/Userland/Libraries/LibWeb/Loader/Resource.h new file mode 100644 index 0000000000..9838071c65 --- /dev/null +++ b/Userland/Libraries/LibWeb/Loader/Resource.h @@ -0,0 +1,117 @@ +/* + * 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 <AK/ByteBuffer.h> +#include <AK/HashMap.h> +#include <AK/HashTable.h> +#include <AK/Noncopyable.h> +#include <AK/RefCounted.h> +#include <AK/URL.h> +#include <AK/WeakPtr.h> +#include <AK/Weakable.h> +#include <LibGfx/Forward.h> +#include <LibWeb/Forward.h> +#include <LibWeb/Loader/LoadRequest.h> + +namespace Web { + +class ResourceClient; + +class Resource : public RefCounted<Resource> { + AK_MAKE_NONCOPYABLE(Resource); + AK_MAKE_NONMOVABLE(Resource); + +public: + enum class Type { + Generic, + Image, + }; + + static NonnullRefPtr<Resource> create(Badge<ResourceLoader>, Type, const LoadRequest&); + virtual ~Resource(); + + Type type() const { return m_type; } + + bool is_loaded() const { return m_loaded; } + + bool is_failed() const { return m_failed; } + const String& error() const { return m_error; } + + bool has_encoded_data() const { return !m_encoded_data.is_null(); } + + const URL& url() const { return m_request.url(); } + const ByteBuffer& encoded_data() const { return m_encoded_data; } + + const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers() const { return m_response_headers; } + + void register_client(Badge<ResourceClient>, ResourceClient&); + void unregister_client(Badge<ResourceClient>, ResourceClient&); + + const String& encoding() const { return m_encoding; } + const String& mime_type() const { return m_mime_type; } + + void for_each_client(Function<void(ResourceClient&)>); + + void did_load(Badge<ResourceLoader>, ReadonlyBytes data, const HashMap<String, String, CaseInsensitiveStringTraits>& headers); + void did_fail(Badge<ResourceLoader>, const String& error); + +protected: + explicit Resource(Type, const LoadRequest&); + +private: + LoadRequest m_request; + ByteBuffer m_encoded_data; + Type m_type { Type::Generic }; + bool m_loaded { false }; + bool m_failed { false }; + String m_error; + String m_encoding; + String m_mime_type; + HashMap<String, String, CaseInsensitiveStringTraits> m_response_headers; + HashTable<ResourceClient*> m_clients; +}; + +class ResourceClient : public Weakable<ResourceClient> { +public: + virtual ~ResourceClient(); + + virtual void resource_did_load() { } + virtual void resource_did_fail() { } + +protected: + virtual Resource::Type client_type() const { return Resource::Type::Generic; } + + Resource* resource() { return m_resource; } + const Resource* resource() const { return m_resource; } + void set_resource(Resource*); + +private: + RefPtr<Resource> m_resource; +}; + +} diff --git a/Userland/Libraries/LibWeb/Loader/ResourceLoader.cpp b/Userland/Libraries/LibWeb/Loader/ResourceLoader.cpp new file mode 100644 index 0000000000..74ec1194d4 --- /dev/null +++ b/Userland/Libraries/LibWeb/Loader/ResourceLoader.cpp @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2018-2021, 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/Base64.h> +#include <AK/JsonObject.h> +#include <AK/SharedBuffer.h> +#include <LibCore/EventLoop.h> +#include <LibCore/File.h> +#include <LibProtocol/Client.h> +#include <LibProtocol/Download.h> +#include <LibWeb/Loader/ContentFilter.h> +#include <LibWeb/Loader/LoadRequest.h> +#include <LibWeb/Loader/Resource.h> +#include <LibWeb/Loader/ResourceLoader.h> + +//#define CACHE_DEBUG + +namespace Web { + +ResourceLoader& ResourceLoader::the() +{ + static ResourceLoader* s_the; + if (!s_the) + s_the = &ResourceLoader::construct().leak_ref(); + return *s_the; +} + +ResourceLoader::ResourceLoader() + : m_protocol_client(Protocol::Client::construct()) + , m_user_agent("Mozilla/4.0 (SerenityOS; x86) LibWeb+LibJS (Not KHTML, nor Gecko) LibWeb") +{ +} + +void ResourceLoader::load_sync(const URL& url, Function<void(ReadonlyBytes, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback) +{ + Core::EventLoop loop; + + load( + url, + [&](auto data, auto& response_headers) { + success_callback(data, response_headers); + loop.quit(0); + }, + [&](auto& string) { + if (error_callback) + error_callback(string); + loop.quit(0); + }); + + loop.exec(); +} + +static HashMap<LoadRequest, NonnullRefPtr<Resource>> s_resource_cache; + +RefPtr<Resource> ResourceLoader::load_resource(Resource::Type type, const LoadRequest& request) +{ + if (!request.is_valid()) + return nullptr; + + auto it = s_resource_cache.find(request); + if (it != s_resource_cache.end()) { + if (it->value->type() != type) { + dbg() << "FIXME: Not using cached resource for " << request.url() << " since there's a type mismatch."; + } else { +#ifdef CACHE_DEBUG + dbg() << "Reusing cached resource for: " << request.url(); +#endif + return it->value; + } + } + + auto resource = Resource::create({}, type, request); + + s_resource_cache.set(request, resource); + + load( + request, + [=](auto data, auto& headers) { + const_cast<Resource&>(*resource).did_load({}, data, headers); + }, + [=](auto& error) { + const_cast<Resource&>(*resource).did_fail({}, error); + }); + + return resource; +} + +void ResourceLoader::load(const LoadRequest& request, Function<void(ReadonlyBytes, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback) +{ + auto& url = request.url(); + + if (is_port_blocked(url.port())) { + dbg() << "ResourceLoader::load: Error: blocked port " << url.port() << " for URL: " << url; + return; + } + + if (ContentFilter::the().is_filtered(url)) { + dbgln("\033[32;1mResourceLoader::load: URL was filtered! {}\033[0m", url); + error_callback("URL was filtered"); + return; + } + + if (url.protocol() == "about") { + dbg() << "Loading about: URL " << url; + deferred_invoke([success_callback = move(success_callback)](auto&) { + success_callback(String::empty().to_byte_buffer(), {}); + }); + return; + } + + if (url.protocol() == "data") { + dbg() << "ResourceLoader loading a data URL with mime-type: '" << url.data_mime_type() << "', base64=" << url.data_payload_is_base64() << ", payload='" << url.data_payload() << "'"; + + ByteBuffer data; + if (url.data_payload_is_base64()) + data = decode_base64(url.data_payload()); + else + data = url.data_payload().to_byte_buffer(); + + deferred_invoke([data = move(data), success_callback = move(success_callback)](auto&) { + success_callback(data, {}); + }); + return; + } + + if (url.protocol() == "file") { + auto f = Core::File::construct(); + f->set_filename(url.path()); + if (!f->open(Core::IODevice::OpenMode::ReadOnly)) { + dbg() << "ResourceLoader::load: Error: " << f->error_string(); + if (error_callback) + error_callback(f->error_string()); + return; + } + + auto data = f->read_all(); + deferred_invoke([data = move(data), success_callback = move(success_callback)](auto&) { + success_callback(data, {}); + }); + return; + } + + if (url.protocol() == "http" || url.protocol() == "https" || url.protocol() == "gemini") { + HashMap<String, String> headers; + headers.set("User-Agent", m_user_agent); + headers.set("Accept-Encoding", "gzip"); + + for (auto& it : request.headers()) { + headers.set(it.key, it.value); + } + + auto download = protocol_client().start_download(request.method(), url.to_string(), headers, request.body()); + if (!download) { + if (error_callback) + error_callback("Failed to initiate load"); + return; + } + download->on_buffered_download_finish = [this, success_callback = move(success_callback), error_callback = move(error_callback), download](bool success, auto, auto& response_headers, auto status_code, ReadonlyBytes payload) { + if (status_code.has_value() && status_code.value() >= 400 && status_code.value() <= 499) { + if (error_callback) + error_callback(String::formatted("HTTP error ({})", status_code.value())); + return; + } + --m_pending_loads; + if (on_load_counter_change) + on_load_counter_change(); + if (!success) { + if (error_callback) + error_callback("HTTP load failed"); + return; + } + deferred_invoke([download](auto&) { + // Clear circular reference of `download` captured by copy + const_cast<Protocol::Download&>(*download).on_buffered_download_finish = nullptr; + }); + success_callback(payload, response_headers); + }; + download->set_should_buffer_all_input(true); + download->on_certificate_requested = []() -> Protocol::Download::CertificateAndKey { + return {}; + }; + ++m_pending_loads; + if (on_load_counter_change) + on_load_counter_change(); + return; + } + + if (error_callback) + error_callback(String::formatted("Protocol not implemented: {}", url.protocol())); +} + +void ResourceLoader::load(const URL& url, Function<void(ReadonlyBytes, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback) +{ + LoadRequest request; + request.set_url(url); + load(request, move(success_callback), move(error_callback)); +} + +bool ResourceLoader::is_port_blocked(int port) +{ + int ports[] { 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, + 43, 53, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111, 113, + 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512, 513, 514, + 515, 526, 530, 531, 532, 540, 556, 563, 587, 601, 636, 993, 995, + 2049, 3659, 4045, 6000, 6379, 6665, 6666, 6667, 6668, 6669, 9000 }; + for (auto blocked_port : ports) + if (port == blocked_port) + return true; + return false; +} + +} diff --git a/Userland/Libraries/LibWeb/Loader/ResourceLoader.h b/Userland/Libraries/LibWeb/Loader/ResourceLoader.h new file mode 100644 index 0000000000..5fe23adc8a --- /dev/null +++ b/Userland/Libraries/LibWeb/Loader/ResourceLoader.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018-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 <AK/Function.h> +#include <AK/URL.h> +#include <LibCore/Object.h> +#include <LibWeb/Loader/Resource.h> + +namespace Protocol { +class Client; +} + +namespace Web { + +class ResourceLoader : public Core::Object { + C_OBJECT(ResourceLoader) +public: + static ResourceLoader& the(); + + RefPtr<Resource> load_resource(Resource::Type, const LoadRequest&); + + void load(const LoadRequest&, Function<void(ReadonlyBytes, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback = nullptr); + void load(const URL&, Function<void(ReadonlyBytes, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback = nullptr); + void load_sync(const URL&, Function<void(ReadonlyBytes, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback = nullptr); + + Function<void()> on_load_counter_change; + + int pending_loads() const { return m_pending_loads; } + + Protocol::Client& protocol_client() { return *m_protocol_client; } + + const String& user_agent() const { return m_user_agent; } + +private: + ResourceLoader(); + static bool is_port_blocked(int port); + + int m_pending_loads { 0 }; + + RefPtr<Protocol::Client> m_protocol_client; + String m_user_agent; +}; + +} diff --git a/Userland/Libraries/LibWeb/Namespace.cpp b/Userland/Libraries/LibWeb/Namespace.cpp new file mode 100644 index 0000000000..b0a1292804 --- /dev/null +++ b/Userland/Libraries/LibWeb/Namespace.cpp @@ -0,0 +1,49 @@ +/* + * 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 <LibWeb/Namespace.h> + +namespace Web::Namespace { + +#define __ENUMERATE_NAMESPACE(name, namespace_) FlyString name; +ENUMERATE_NAMESPACES +#undef __ENUMERATE_NAMESPACE + +[[gnu::constructor]] static void initialize() +{ + static bool s_initialized = false; + if (s_initialized) + return; + +#define __ENUMERATE_NAMESPACE(name, namespace_) \ + name = namespace_; + ENUMERATE_NAMESPACES +#undef __ENUMERATE_NAMESPACE + + s_initialized = true; +} + +} diff --git a/Userland/Libraries/LibWeb/Namespace.h b/Userland/Libraries/LibWeb/Namespace.h new file mode 100644 index 0000000000..e7fe6ff0f7 --- /dev/null +++ b/Userland/Libraries/LibWeb/Namespace.h @@ -0,0 +1,45 @@ +/* + * 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 <AK/FlyString.h> + +namespace Web::Namespace { + +#define ENUMERATE_NAMESPACES \ + __ENUMERATE_NAMESPACE(HTML, "http://www.w3.org/1999/xhtml") \ + __ENUMERATE_NAMESPACE(MathML, "http://www.w3.org/1998/Math/MathML") \ + __ENUMERATE_NAMESPACE(SVG, "http://www.w3.org/2000/svg") \ + __ENUMERATE_NAMESPACE(XLink, "http://www.w3.org/1999/xlink") \ + __ENUMERATE_NAMESPACE(XML, "http://www.w3.org/XML/1998/namespace") \ + __ENUMERATE_NAMESPACE(XMLNS, "http://www.w3.org/2000/xmlns/") + +#define __ENUMERATE_NAMESPACE(name, namespace_) extern FlyString name; +ENUMERATE_NAMESPACES +#undef __ENUMERATE_NAMESPACE + +} diff --git a/Userland/Libraries/LibWeb/Origin.h b/Userland/Libraries/LibWeb/Origin.h new file mode 100644 index 0000000000..5a4b061b33 --- /dev/null +++ b/Userland/Libraries/LibWeb/Origin.h @@ -0,0 +1,62 @@ +/* + * 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 <AK/String.h> + +namespace Web { + +class Origin { +public: + Origin() { } + Origin(const String& protocol, const String& host, u16 port) + : m_protocol(protocol) + , m_host(host) + , m_port(port) + { + } + + bool is_null() const { return m_protocol.is_null() && m_host.is_null() && !m_port; } + + const String& protocol() const { return m_protocol; } + const String& host() const { return m_host; } + u16 port() const { return m_port; } + + bool is_same(const Origin& other) const + { + return protocol() == other.protocol() + && host() == other.host() + && port() == other.port(); + } + +private: + String m_protocol; + String m_host; + u16 m_port { 0 }; +}; + +} diff --git a/Userland/Libraries/LibWeb/OutOfProcessWebView.cpp b/Userland/Libraries/LibWeb/OutOfProcessWebView.cpp new file mode 100644 index 0000000000..d30025d678 --- /dev/null +++ b/Userland/Libraries/LibWeb/OutOfProcessWebView.cpp @@ -0,0 +1,257 @@ +/* + * 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 "OutOfProcessWebView.h" +#include "WebContentClient.h" +#include <AK/SharedBuffer.h> +#include <LibGUI/MessageBox.h> +#include <LibGUI/Painter.h> +#include <LibGUI/ScrollBar.h> +#include <LibGUI/Window.h> +#include <LibGfx/Palette.h> +#include <LibGfx/SystemTheme.h> + +REGISTER_WIDGET(Web, OutOfProcessWebView) + +namespace Web { + +OutOfProcessWebView::OutOfProcessWebView() +{ + set_should_hide_unnecessary_scrollbars(true); + set_focus_policy(GUI::FocusPolicy::StrongFocus); + m_client = WebContentClient::construct(*this); + client().post_message(Messages::WebContentServer::UpdateSystemTheme(Gfx::current_system_theme_buffer_id())); +} + +OutOfProcessWebView::~OutOfProcessWebView() +{ +} + +void OutOfProcessWebView::load(const URL& url) +{ + m_url = url; + client().post_message(Messages::WebContentServer::LoadURL(url)); +} + +void OutOfProcessWebView::load_html(const StringView& html, const URL& url) +{ + m_url = url; + client().post_message(Messages::WebContentServer::LoadHTML(html, url)); +} + +void OutOfProcessWebView::load_empty_document() +{ + m_url = {}; + client().post_message(Messages::WebContentServer::LoadHTML("", {})); +} + +void OutOfProcessWebView::paint_event(GUI::PaintEvent& event) +{ + GUI::ScrollableWidget::paint_event(event); + + // If the available size is empty, we don't have a front or back bitmap to draw. + if (available_size().is_empty()) + return; + + GUI::Painter painter(*this); + painter.add_clip_rect(event.rect()); + + if (!m_has_usable_bitmap) { + painter.fill_rect(frame_inner_rect(), palette().base()); + return; + } + + painter.add_clip_rect(frame_inner_rect()); + painter.translate(frame_thickness(), frame_thickness()); + + ASSERT(m_front_bitmap); + painter.blit({ 0, 0 }, *m_front_bitmap, m_front_bitmap->rect()); +} + +void OutOfProcessWebView::resize_event(GUI::ResizeEvent& event) +{ + GUI::ScrollableWidget::resize_event(event); + + client().post_message(Messages::WebContentServer::SetViewportRect(Gfx::IntRect({ horizontal_scrollbar().value(), vertical_scrollbar().value() }, available_size()))); + + m_front_bitmap = nullptr; + m_back_bitmap = nullptr; + m_has_usable_bitmap = false; + + if (available_size().is_empty()) + return; + + if (auto new_bitmap = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::RGB32, available_size())) { + new_bitmap->shared_buffer()->share_with(client().server_pid()); + m_front_bitmap = move(new_bitmap); + } + + if (auto new_bitmap = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::RGB32, available_size())) { + new_bitmap->shared_buffer()->share_with(client().server_pid()); + m_back_bitmap = move(new_bitmap); + } + + request_repaint(); +} + +void OutOfProcessWebView::keydown_event(GUI::KeyEvent& event) +{ + client().post_message(Messages::WebContentServer::KeyDown(event.key(), event.modifiers(), event.code_point())); +} + +void OutOfProcessWebView::mousedown_event(GUI::MouseEvent& event) +{ + client().post_message(Messages::WebContentServer::MouseDown(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers())); +} + +void OutOfProcessWebView::mouseup_event(GUI::MouseEvent& event) +{ + client().post_message(Messages::WebContentServer::MouseUp(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers())); +} + +void OutOfProcessWebView::mousemove_event(GUI::MouseEvent& event) +{ + client().post_message(Messages::WebContentServer::MouseMove(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers())); +} + +void OutOfProcessWebView::theme_change_event(GUI::ThemeChangeEvent& event) +{ + GUI::ScrollableWidget::theme_change_event(event); + client().post_message(Messages::WebContentServer::UpdateSystemTheme(Gfx::current_system_theme_buffer_id())); + request_repaint(); +} + +void OutOfProcessWebView::notify_server_did_paint(Badge<WebContentClient>, i32 shbuf_id) +{ + if (m_back_bitmap->shbuf_id() == shbuf_id) { + m_has_usable_bitmap = true; + swap(m_back_bitmap, m_front_bitmap); + update(); + } +} + +void OutOfProcessWebView::notify_server_did_invalidate_content_rect(Badge<WebContentClient>, [[maybe_unused]] const Gfx::IntRect& content_rect) +{ + request_repaint(); +} + +void OutOfProcessWebView::notify_server_did_change_selection(Badge<WebContentClient>) +{ + request_repaint(); +} + +void OutOfProcessWebView::notify_server_did_layout(Badge<WebContentClient>, const Gfx::IntSize& content_size) +{ + set_content_size(content_size); +} + +void OutOfProcessWebView::notify_server_did_change_title(Badge<WebContentClient>, const String& title) +{ + if (on_title_change) + on_title_change(title); +} + +void OutOfProcessWebView::notify_server_did_request_scroll_into_view(Badge<WebContentClient>, const Gfx::IntRect& rect) +{ + scroll_into_view(rect, true, true); +} + +void OutOfProcessWebView::notify_server_did_hover_link(Badge<WebContentClient>, const URL& url) +{ + set_override_cursor(Gfx::StandardCursor::Hand); + if (on_link_hover) + on_link_hover(url); +} + +void OutOfProcessWebView::notify_server_did_unhover_link(Badge<WebContentClient>) +{ + set_override_cursor(Gfx::StandardCursor::None); + if (on_link_hover) + on_link_hover({}); +} + +void OutOfProcessWebView::notify_server_did_click_link(Badge<WebContentClient>, const URL& url, const String& target, unsigned int modifiers) +{ + if (on_link_click) + on_link_click(url, target, modifiers); +} + +void OutOfProcessWebView::notify_server_did_middle_click_link(Badge<WebContentClient>, const URL& url, const String& target, unsigned int modifiers) +{ + if (on_link_middle_click) + on_link_middle_click(url, target, modifiers); +} + +void OutOfProcessWebView::notify_server_did_start_loading(Badge<WebContentClient>, const URL& url) +{ + if (on_load_start) + on_load_start(url); +} + +void OutOfProcessWebView::notify_server_did_finish_loading(Badge<WebContentClient>, const URL& url) +{ + if (on_load_finish) + on_load_finish(url); +} + +void OutOfProcessWebView::notify_server_did_request_context_menu(Badge<WebContentClient>, const Gfx::IntPoint& content_position) +{ + if (on_context_menu_request) + on_context_menu_request(screen_relative_rect().location().translated(to_widget_position(content_position))); +} + +void OutOfProcessWebView::notify_server_did_request_link_context_menu(Badge<WebContentClient>, const Gfx::IntPoint& content_position, const URL& url, const String&, unsigned) +{ + if (on_link_context_menu_request) + on_link_context_menu_request(url, screen_relative_rect().location().translated(to_widget_position(content_position))); +} + +void OutOfProcessWebView::notify_server_did_request_alert(Badge<WebContentClient>, const String& message) +{ + GUI::MessageBox::show(window(), message, "Alert", GUI::MessageBox::Type::Information); +} + +void OutOfProcessWebView::did_scroll() +{ + client().post_message(Messages::WebContentServer::SetViewportRect(visible_content_rect())); + request_repaint(); +} + +void OutOfProcessWebView::request_repaint() +{ + // If this widget was instantiated but not yet added to a window, + // it won't have a back bitmap yet, so we can just skip repaint requests. + if (!m_back_bitmap) + return; + client().post_message(Messages::WebContentServer::Paint(m_back_bitmap->rect().translated(horizontal_scrollbar().value(), vertical_scrollbar().value()), m_back_bitmap->shbuf_id())); +} + +WebContentClient& OutOfProcessWebView::client() +{ + return *m_client; +} + +} diff --git a/Userland/Libraries/LibWeb/OutOfProcessWebView.h b/Userland/Libraries/LibWeb/OutOfProcessWebView.h new file mode 100644 index 0000000000..a742a01af8 --- /dev/null +++ b/Userland/Libraries/LibWeb/OutOfProcessWebView.h @@ -0,0 +1,96 @@ +/* + * 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 <AK/URL.h> +#include <LibGUI/ScrollableWidget.h> +#include <LibGUI/Widget.h> +#include <LibWeb/WebViewHooks.h> + +namespace Web { + +class WebContentClient; + +class OutOfProcessWebView final + : public GUI::ScrollableWidget + , public Web::WebViewHooks { + C_OBJECT(OutOfProcessWebView); + +public: + virtual ~OutOfProcessWebView() override; + + URL url() const { return m_url; } + void load(const URL&); + + void load_html(const StringView&, const URL&); + void load_empty_document(); + + void notify_server_did_layout(Badge<WebContentClient>, const Gfx::IntSize& content_size); + void notify_server_did_paint(Badge<WebContentClient>, i32 shbuf_id); + void notify_server_did_invalidate_content_rect(Badge<WebContentClient>, const Gfx::IntRect&); + void notify_server_did_change_selection(Badge<WebContentClient>); + void notify_server_did_change_title(Badge<WebContentClient>, const String&); + void notify_server_did_request_scroll_into_view(Badge<WebContentClient>, const Gfx::IntRect&); + void notify_server_did_hover_link(Badge<WebContentClient>, const URL&); + void notify_server_did_unhover_link(Badge<WebContentClient>); + void notify_server_did_click_link(Badge<WebContentClient>, const URL&, const String& target, unsigned modifiers); + void notify_server_did_middle_click_link(Badge<WebContentClient>, const URL&, const String& target, unsigned modifiers); + void notify_server_did_start_loading(Badge<WebContentClient>, const URL&); + void notify_server_did_finish_loading(Badge<WebContentClient>, const URL&); + void notify_server_did_request_context_menu(Badge<WebContentClient>, const Gfx::IntPoint&); + void notify_server_did_request_link_context_menu(Badge<WebContentClient>, const Gfx::IntPoint&, const URL&, const String& target, unsigned modifiers); + void notify_server_did_request_alert(Badge<WebContentClient>, const String& message); + +private: + OutOfProcessWebView(); + + // ^Widget + virtual void paint_event(GUI::PaintEvent&) override; + virtual void resize_event(GUI::ResizeEvent&) override; + virtual void mousedown_event(GUI::MouseEvent&) override; + virtual void mouseup_event(GUI::MouseEvent&) override; + virtual void mousemove_event(GUI::MouseEvent&) override; + virtual void keydown_event(GUI::KeyEvent&) override; + virtual void theme_change_event(GUI::ThemeChangeEvent&) override; + + // ^ScrollableWidget + virtual void did_scroll() override; + + void request_repaint(); + + WebContentClient& client(); + + URL m_url; + + RefPtr<WebContentClient> m_client; + RefPtr<Gfx::Bitmap> m_front_bitmap; + RefPtr<Gfx::Bitmap> m_back_bitmap; + + bool m_has_usable_bitmap { false }; +}; + +} diff --git a/Userland/Libraries/LibWeb/Page/EditEventHandler.cpp b/Userland/Libraries/LibWeb/Page/EditEventHandler.cpp new file mode 100644 index 0000000000..5d27efe2cf --- /dev/null +++ b/Userland/Libraries/LibWeb/Page/EditEventHandler.cpp @@ -0,0 +1,124 @@ +/* + * 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 <AK/StringBuilder.h> +#include <LibWeb/DOM/Position.h> +#include <LibWeb/DOM/Range.h> +#include <LibWeb/DOM/Text.h> +#include <LibWeb/Layout/LayoutPosition.h> +#include <LibWeb/Page/Frame.h> + +#include <LibWeb/DOM/Document.h> +#include <LibWeb/Dump.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> + +#include "EditEventHandler.h" + +namespace Web { + +// This method is quite convoluted but this is necessary to make editing feel intuitive. +void EditEventHandler::handle_delete(DOM::Range& range) +{ + auto* start = downcast<DOM::Text>(range.start_container()); + auto* end = downcast<DOM::Text>(range.end_container()); + + if (start == end) { + StringBuilder builder; + builder.append(start->data().substring_view(0, range.start_offset())); + builder.append(end->data().substring_view(range.end_offset())); + + start->set_data(builder.to_string()); + } else { + // Remove all the nodes that are fully enclosed in the range. + HashTable<DOM::Node*> queued_for_deletion; + for (auto* node = start->next_in_pre_order(); node; node = node->next_in_pre_order()) { + if (node == end) + break; + + queued_for_deletion.set(node); + } + for (auto* parent = start->parent(); parent; parent = parent->parent()) + queued_for_deletion.remove(parent); + for (auto* parent = end->parent(); parent; parent = parent->parent()) + queued_for_deletion.remove(parent); + for (auto* node : queued_for_deletion) + node->parent()->remove_child(*node); + + // Join the parent nodes of start and end. + DOM::Node *insert_after = start, *remove_from = end, *parent_of_end = end->parent(); + while (remove_from) { + auto* next_sibling = remove_from->next_sibling(); + + remove_from->parent()->remove_child(*remove_from); + insert_after->parent()->insert_before(*remove_from, *insert_after); + + insert_after = remove_from; + remove_from = next_sibling; + } + if (!parent_of_end->has_children()) { + if (parent_of_end->parent()) + parent_of_end->parent()->remove_child(*parent_of_end); + } + + // Join the start and end nodes. + StringBuilder builder; + builder.append(start->data().substring_view(0, range.start_offset())); + builder.append(end->data().substring_view(range.end_offset())); + + start->set_data(builder.to_string()); + start->parent()->remove_child(*end); + } + + // FIXME: When nodes are removed from the DOM, the associated layout nodes become stale and still + // remain in the layout tree. This has to be fixed, this just causes everything to be recomputed + // which really hurts performance. + m_frame.document()->force_layout(); + + m_frame.did_edit({}); +} + +void EditEventHandler::handle_insert(DOM::Position position, u32 code_point) +{ + if (is<DOM::Text>(*position.node())) { + auto& node = downcast<DOM::Text>(*position.node()); + + StringBuilder builder; + builder.append(node.data().substring_view(0, position.offset())); + builder.append_code_point(code_point); + builder.append(node.data().substring_view(position.offset())); + node.set_data(builder.to_string()); + + node.invalidate_style(); + } + + // FIXME: When nodes are removed from the DOM, the associated layout nodes become stale and still + // remain in the layout tree. This has to be fixed, this just causes everything to be recomputed + // which really hurts performance. + m_frame.document()->force_layout(); + + m_frame.did_edit({}); +} +} diff --git a/Userland/Libraries/LibWeb/Page/EditEventHandler.h b/Userland/Libraries/LibWeb/Page/EditEventHandler.h new file mode 100644 index 0000000000..67edb9eff0 --- /dev/null +++ b/Userland/Libraries/LibWeb/Page/EditEventHandler.h @@ -0,0 +1,49 @@ +/* + * 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 <LibWeb/Forward.h> + +namespace Web { + +class EditEventHandler { +public: + explicit EditEventHandler(Frame& frame) + : m_frame(frame) + { + } + + virtual ~EditEventHandler() = default; + + virtual void handle_delete(DOM::Range&); + virtual void handle_insert(DOM::Position, u32 code_point); + +private: + Frame& m_frame; +}; + +} diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.cpp b/Userland/Libraries/LibWeb/Page/EventHandler.cpp new file mode 100644 index 0000000000..7a69085aa4 --- /dev/null +++ b/Userland/Libraries/LibWeb/Page/EventHandler.cpp @@ -0,0 +1,414 @@ +/* + * 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 <LibGUI/Event.h> +#include <LibGUI/Window.h> +#include <LibJS/Runtime/Value.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Range.h> +#include <LibWeb/DOM/Text.h> +#include <LibWeb/HTML/HTMLAnchorElement.h> +#include <LibWeb/HTML/HTMLIFrameElement.h> +#include <LibWeb/HTML/HTMLImageElement.h> +#include <LibWeb/InProcessWebView.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> +#include <LibWeb/Page/EventHandler.h> +#include <LibWeb/Page/Frame.h> +#include <LibWeb/UIEvents/EventNames.h> +#include <LibWeb/UIEvents/MouseEvent.h> + +namespace Web { + +static Gfx::IntPoint compute_mouse_event_offset(const Gfx::IntPoint& position, const Layout::Node& layout_node) +{ + auto top_left_of_layout_node = layout_node.box_type_agnostic_position(); + return { + position.x() - static_cast<int>(top_left_of_layout_node.x()), + position.y() - static_cast<int>(top_left_of_layout_node.y()) + }; +} + +EventHandler::EventHandler(Badge<Frame>, Frame& frame) + : m_frame(frame) + , m_edit_event_handler(make<EditEventHandler>(frame)) +{ +} + +EventHandler::~EventHandler() +{ +} + +const Layout::InitialContainingBlockBox* EventHandler::layout_root() const +{ + if (!m_frame.document()) + return nullptr; + return m_frame.document()->layout_node(); +} + +Layout::InitialContainingBlockBox* EventHandler::layout_root() +{ + if (!m_frame.document()) + return nullptr; + return m_frame.document()->layout_node(); +} + +bool EventHandler::handle_mouseup(const Gfx::IntPoint& position, unsigned button, unsigned modifiers) +{ + if (!layout_root()) + return false; + + if (m_mouse_event_tracking_layout_node) { + m_mouse_event_tracking_layout_node->handle_mouseup({}, position, button, modifiers); + return true; + } + + bool handled_event = false; + + auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact); + + if (result.layout_node && result.layout_node->wants_mouse_events()) { + result.layout_node->handle_mouseup({}, position, button, modifiers); + + // Things may have changed as a consequence of Layout::Node::handle_mouseup(). Hit test again. + if (!layout_root()) + return true; + result = layout_root()->hit_test(position, Layout::HitTestType::Exact); + } + + if (result.layout_node && result.layout_node->dom_node()) { + RefPtr<DOM::Node> node = result.layout_node->dom_node(); + if (is<HTML::HTMLIFrameElement>(*node)) { + if (auto* subframe = downcast<HTML::HTMLIFrameElement>(*node).content_frame()) + return subframe->event_handler().handle_mouseup(position.translated(compute_mouse_event_offset({}, *result.layout_node)), button, modifiers); + return false; + } + auto offset = compute_mouse_event_offset(position, *result.layout_node); + node->dispatch_event(UIEvents::MouseEvent::create(UIEvents::EventNames::mouseup, offset.x(), offset.y())); + handled_event = true; + } + + if (button == GUI::MouseButton::Left) + m_in_mouse_selection = false; + return handled_event; +} + +bool EventHandler::handle_mousedown(const Gfx::IntPoint& position, unsigned button, unsigned modifiers) +{ + if (!layout_root()) + return false; + + if (m_mouse_event_tracking_layout_node) { + m_mouse_event_tracking_layout_node->handle_mousedown({}, position, button, modifiers); + return true; + } + + NonnullRefPtr document = *m_frame.document(); + RefPtr<DOM::Node> node; + + { + auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact); + if (!result.layout_node) + return false; + + node = result.layout_node->dom_node(); + document->set_hovered_node(node); + + if (result.layout_node->wants_mouse_events()) { + result.layout_node->handle_mousedown({}, position, button, modifiers); + return true; + } + + if (!node) + return false; + + if (is<HTML::HTMLIFrameElement>(*node)) { + if (auto* subframe = downcast<HTML::HTMLIFrameElement>(*node).content_frame()) + return subframe->event_handler().handle_mousedown(position.translated(compute_mouse_event_offset({}, *result.layout_node)), button, modifiers); + return false; + } + + if (auto* page = m_frame.page()) + page->set_focused_frame({}, m_frame); + + auto offset = compute_mouse_event_offset(position, *result.layout_node); + node->dispatch_event(UIEvents::MouseEvent::create(UIEvents::EventNames::mousedown, offset.x(), offset.y())); + } + + // NOTE: Dispatching an event may have disturbed the world. + if (!layout_root() || layout_root() != node->document().layout_node()) + return true; + + if (button == GUI::MouseButton::Right && is<HTML::HTMLImageElement>(*node)) { + auto& image_element = downcast<HTML::HTMLImageElement>(*node); + auto image_url = image_element.document().complete_url(image_element.src()); + if (auto* page = m_frame.page()) + page->client().page_did_request_image_context_menu(m_frame.to_main_frame_position(position), image_url, "", modifiers, image_element.bitmap()); + return true; + } + + if (RefPtr<HTML::HTMLAnchorElement> link = node->enclosing_link_element()) { + auto href = link->href(); + auto url = document->complete_url(href); + dbgln("Web::EventHandler: Clicking on a link to {}", url); + if (button == GUI::MouseButton::Left) { + auto href = link->href(); + auto url = document->complete_url(href); + if (href.starts_with("javascript:")) { + document->run_javascript(href.substring_view(11, href.length() - 11)); + } else if (href.starts_with('#')) { + auto anchor = href.substring_view(1, href.length() - 1); + m_frame.scroll_to_anchor(anchor); + } else { + if (m_frame.is_main_frame()) { + if (auto* page = m_frame.page()) + page->client().page_did_click_link(url, link->target(), modifiers); + } else { + // FIXME: Handle different targets! + m_frame.loader().load(url, FrameLoader::Type::Navigation); + } + } + } else if (button == GUI::MouseButton::Right) { + if (auto* page = m_frame.page()) + page->client().page_did_request_link_context_menu(m_frame.to_main_frame_position(position), url, link->target(), modifiers); + } else if (button == GUI::MouseButton::Middle) { + if (auto* page = m_frame.page()) + page->client().page_did_middle_click_link(url, link->target(), modifiers); + } + } else { + if (button == GUI::MouseButton::Left) { + auto result = layout_root()->hit_test(position, Layout::HitTestType::TextCursor); + if (result.layout_node && result.layout_node->dom_node()) { + m_frame.set_cursor_position(DOM::Position(*node, result.index_in_node)); + layout_root()->set_selection({ { result.layout_node, result.index_in_node }, {} }); + m_in_mouse_selection = true; + } + } else if (button == GUI::MouseButton::Right) { + if (auto* page = m_frame.page()) + page->client().page_did_request_context_menu(m_frame.to_main_frame_position(position)); + } + } + return true; +} + +bool EventHandler::handle_mousemove(const Gfx::IntPoint& position, unsigned buttons, unsigned modifiers) +{ + if (!layout_root()) + return false; + + if (m_mouse_event_tracking_layout_node) { + m_mouse_event_tracking_layout_node->handle_mousemove({}, position, buttons, modifiers); + return true; + } + + auto& document = *m_frame.document(); + + bool hovered_node_changed = false; + bool is_hovering_link = false; + bool is_hovering_text = false; + auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact); + const HTML::HTMLAnchorElement* hovered_link_element = nullptr; + if (result.layout_node) { + + if (result.layout_node->wants_mouse_events()) { + document.set_hovered_node(result.layout_node->dom_node()); + result.layout_node->handle_mousemove({}, position, buttons, modifiers); + // FIXME: It feels a bit aggressive to always update the cursor like this. + if (auto* page = m_frame.page()) + page->client().page_did_request_cursor_change(Gfx::StandardCursor::None); + return true; + } + + RefPtr<DOM::Node> node = result.layout_node->dom_node(); + + if (node && is<HTML::HTMLIFrameElement>(*node)) { + if (auto* subframe = downcast<HTML::HTMLIFrameElement>(*node).content_frame()) + return subframe->event_handler().handle_mousemove(position.translated(compute_mouse_event_offset({}, *result.layout_node)), buttons, modifiers); + return false; + } + + hovered_node_changed = node != document.hovered_node(); + document.set_hovered_node(node); + if (node) { + if (node->is_text()) + is_hovering_text = true; + hovered_link_element = node->enclosing_link_element(); + if (hovered_link_element) + is_hovering_link = true; + auto offset = compute_mouse_event_offset(position, *result.layout_node); + node->dispatch_event(UIEvents::MouseEvent::create(UIEvents::EventNames::mousemove, offset.x(), offset.y())); + // NOTE: Dispatching an event may have disturbed the world. + if (!layout_root() || layout_root() != node->document().layout_node()) + return true; + } + if (m_in_mouse_selection) { + auto hit = layout_root()->hit_test(position, Layout::HitTestType::TextCursor); + if (hit.layout_node && hit.layout_node->dom_node()) { + layout_root()->set_selection_end({ hit.layout_node, hit.index_in_node }); + } + if (auto* page = m_frame.page()) + page->client().page_did_change_selection(); + } + } + + if (auto* page = m_frame.page()) { + if (is_hovering_link) + page->client().page_did_request_cursor_change(Gfx::StandardCursor::Hand); + else if (is_hovering_text) + page->client().page_did_request_cursor_change(Gfx::StandardCursor::IBeam); + else + page->client().page_did_request_cursor_change(Gfx::StandardCursor::None); + + if (hovered_node_changed) { + RefPtr<HTML::HTMLElement> hovered_html_element = document.hovered_node() ? document.hovered_node()->enclosing_html_element() : nullptr; + if (hovered_html_element && !hovered_html_element->title().is_null()) { + page->client().page_did_enter_tooltip_area(m_frame.to_main_frame_position(position), hovered_html_element->title()); + } else { + page->client().page_did_leave_tooltip_area(); + } + if (is_hovering_link) + page->client().page_did_hover_link(document.complete_url(hovered_link_element->href())); + else + page->client().page_did_unhover_link(); + } + } + return true; +} + +bool EventHandler::focus_next_element() +{ + if (!m_frame.document()) + return false; + auto* element = m_frame.document()->focused_element(); + if (!element) { + element = m_frame.document()->first_child_of_type<DOM::Element>(); + if (element && element->is_focusable()) { + m_frame.document()->set_focused_element(element); + return true; + } + } + + for (element = element->next_element_in_pre_order(); element && !element->is_focusable(); element = element->next_element_in_pre_order()) + ; + + m_frame.document()->set_focused_element(element); + return element; +} + +bool EventHandler::focus_previous_element() +{ + // FIXME: Implement Shift-Tab cycling backwards through focusable elements! + return false; +} + +bool EventHandler::handle_keydown(KeyCode key, unsigned modifiers, u32 code_point) +{ + if (key == KeyCode::Key_Tab) { + if (modifiers & KeyModifier::Mod_Shift) + return focus_previous_element(); + else + return focus_next_element(); + } + + if (layout_root()->selection().is_valid()) { + auto range = layout_root()->selection().to_dom_range()->normalized(); + if (range->start_container()->is_editable()) { + m_frame.document()->layout_node()->set_selection({}); + + // FIXME: This doesn't work for some reason? + m_frame.set_cursor_position({ *range->start_container(), range->start_offset() }); + + if (key == KeyCode::Key_Backspace || key == KeyCode::Key_Delete) { + + m_edit_event_handler->handle_delete(range); + return true; + } else { + + m_edit_event_handler->handle_delete(range); + + m_edit_event_handler->handle_insert(m_frame.cursor_position(), code_point); + m_frame.cursor_position().set_offset(m_frame.cursor_position().offset() + 1); + return true; + } + } + } + + if (m_frame.cursor_position().is_valid() && m_frame.cursor_position().node()->is_editable()) { + if (key == KeyCode::Key_Backspace) { + auto position = m_frame.cursor_position(); + + if (position.offset() == 0) + TODO(); + + m_frame.cursor_position().set_offset(position.offset() - 1); + m_edit_event_handler->handle_delete(DOM::Range::create(*position.node(), position.offset() - 1, *position.node(), position.offset())); + + return true; + } else if (key == KeyCode::Key_Delete) { + auto position = m_frame.cursor_position(); + + if (position.offset() >= downcast<DOM::Text>(position.node())->data().length()) + TODO(); + + m_edit_event_handler->handle_delete(DOM::Range::create(*position.node(), position.offset(), *position.node(), position.offset() + 1)); + + return true; + } else if (key == KeyCode::Key_Right) { + auto position = m_frame.cursor_position(); + + if (position.offset() >= downcast<DOM::Text>(position.node())->data().length()) + TODO(); + + m_frame.cursor_position().set_offset(position.offset() + 1); + + return true; + } else if (key == KeyCode::Key_Left) { + auto position = m_frame.cursor_position(); + + if (position.offset() == 0) + TODO(); + + m_frame.cursor_position().set_offset(position.offset() - 1); + + return true; + } else { + m_edit_event_handler->handle_insert(m_frame.cursor_position(), code_point); + m_frame.cursor_position().set_offset(m_frame.cursor_position().offset() + 1); + return true; + } + } + + return false; +} + +void EventHandler::set_mouse_event_tracking_layout_node(Layout::Node* layout_node) +{ + if (layout_node) + m_mouse_event_tracking_layout_node = layout_node->make_weak_ptr(); + else + m_mouse_event_tracking_layout_node = nullptr; +} + +} diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.h b/Userland/Libraries/LibWeb/Page/EventHandler.h new file mode 100644 index 0000000000..7ea12d6461 --- /dev/null +++ b/Userland/Libraries/LibWeb/Page/EventHandler.h @@ -0,0 +1,72 @@ +/* + * 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 <AK/Forward.h> +#include <AK/WeakPtr.h> +#include <Kernel/API/KeyCode.h> +#include <LibGUI/Forward.h> +#include <LibGfx/Forward.h> +#include <LibWeb/Forward.h> +#include <LibWeb/Page/EditEventHandler.h> + +namespace Web { + +class Frame; + +class EventHandler { +public: + explicit EventHandler(Badge<Frame>, Frame&); + ~EventHandler(); + + bool handle_mouseup(const Gfx::IntPoint&, unsigned button, unsigned modifiers); + bool handle_mousedown(const Gfx::IntPoint&, unsigned button, unsigned modifiers); + bool handle_mousemove(const Gfx::IntPoint&, unsigned buttons, unsigned modifiers); + + bool handle_keydown(KeyCode, unsigned modifiers, u32 code_point); + + void set_mouse_event_tracking_layout_node(Layout::Node*); + + void set_edit_event_handler(NonnullOwnPtr<EditEventHandler> value) { m_edit_event_handler = move(value); } + +private: + bool focus_next_element(); + bool focus_previous_element(); + + Layout::InitialContainingBlockBox* layout_root(); + const Layout::InitialContainingBlockBox* layout_root() const; + + Frame& m_frame; + + bool m_in_mouse_selection { false }; + + WeakPtr<Layout::Node> m_mouse_event_tracking_layout_node; + + NonnullOwnPtr<EditEventHandler> m_edit_event_handler; +}; + +} diff --git a/Userland/Libraries/LibWeb/Page/Frame.cpp b/Userland/Libraries/LibWeb/Page/Frame.cpp new file mode 100644 index 0000000000..8866fea7ef --- /dev/null +++ b/Userland/Libraries/LibWeb/Page/Frame.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2018-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 <LibWeb/DOM/Document.h> +#include <LibWeb/HTML/HTMLAnchorElement.h> +#include <LibWeb/InProcessWebView.h> +#include <LibWeb/Layout/BreakNode.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> +#include <LibWeb/Layout/TextNode.h> +#include <LibWeb/Layout/WidgetBox.h> +#include <LibWeb/Page/Frame.h> + +namespace Web { + +Frame::Frame(DOM::Element& host_element, Frame& main_frame) + : m_page(*main_frame.page()) + , m_main_frame(main_frame) + , m_loader(*this) + , m_event_handler({}, *this) + , m_host_element(host_element) +{ + setup(); +} + +Frame::Frame(Page& page) + : m_page(page) + , m_main_frame(*this) + , m_loader(*this) + , m_event_handler({}, *this) +{ + setup(); +} + +Frame::~Frame() +{ +} + +void Frame::setup() +{ + m_cursor_blink_timer = Core::Timer::construct(500, [this] { + if (!is_focused_frame()) + return; + if (m_cursor_position.node() && m_cursor_position.node()->layout_node()) { + m_cursor_blink_state = !m_cursor_blink_state; + m_cursor_position.node()->layout_node()->set_needs_display(); + } + }); +} + +void Frame::did_edit(Badge<EditEventHandler>) +{ + // The user has edited the content, restart the cursor blink cycle so that + // the cursor doesn't disappear during rapid continuous editing. + m_cursor_blink_state = true; + m_cursor_blink_timer->restart(); +} + +bool Frame::is_focused_frame() const +{ + return m_page && &m_page->focused_frame() == this; +} + +void Frame::set_document(DOM::Document* document) +{ + if (m_document == document) + return; + + m_cursor_position = {}; + + if (m_document) + m_document->detach_from_frame({}, *this); + + m_document = document; + + if (m_document) { + m_document->attach_to_frame({}, *this); + if (m_page) + m_page->client().page_did_change_title(m_document->title()); + } + + if (m_page) + m_page->client().page_did_set_document_in_main_frame(m_document); +} + +void Frame::set_size(const Gfx::IntSize& size) +{ + if (m_size == size) + return; + m_size = size; + if (m_document) + m_document->update_layout(); +} + +void Frame::set_viewport_scroll_offset(const Gfx::IntPoint& offset) +{ + if (m_viewport_scroll_offset == offset) + return; + m_viewport_scroll_offset = offset; + + if (m_document && m_document->layout_node()) + m_document->layout_node()->did_set_viewport_rect({}, viewport_rect()); +} + +void Frame::set_needs_display(const Gfx::IntRect& rect) +{ + if (!viewport_rect().intersects(rect)) + return; + + if (is_main_frame()) { + if (m_page) + m_page->client().page_did_invalidate(to_main_frame_rect(rect)); + return; + } + + if (host_element() && host_element()->layout_node()) + host_element()->layout_node()->set_needs_display(); +} + +void Frame::did_scroll(Badge<InProcessWebView>) +{ + if (!m_document) + return; + if (!m_document->layout_node()) + return; + m_document->layout_node()->for_each_in_subtree_of_type<Layout::WidgetBox>([&](auto& layout_widget) { + layout_widget.update_widget(); + return IterationDecision::Continue; + }); +} + +void Frame::scroll_to_anchor(const String& fragment) +{ + if (!document()) + return; + + auto element = document()->get_element_by_id(fragment); + if (!element) { + auto candidates = document()->get_elements_by_name(fragment); + for (auto& candidate : candidates) { + if (is<HTML::HTMLAnchorElement>(candidate)) { + element = downcast<HTML::HTMLAnchorElement>(candidate); + break; + } + } + } + + // FIXME: This is overly aggressive and should be something more like a "update_layout_if_needed()" + document()->force_layout(); + + if (!element || !element->layout_node()) + return; + + auto& layout_node = *element->layout_node(); + + Gfx::FloatRect float_rect { layout_node.box_type_agnostic_position(), { (float)viewport_rect().width(), (float)viewport_rect().height() } }; + if (is<Layout::Box>(layout_node)) { + auto& layout_box = downcast<Layout::Box>(layout_node); + auto padding_box = layout_box.box_model().padding_box(); + float_rect.move_by(-padding_box.left, -padding_box.top); + } + + if (m_page) + m_page->client().page_did_request_scroll_into_view(enclosing_int_rect(float_rect)); +} + +Gfx::IntRect Frame::to_main_frame_rect(const Gfx::IntRect& a_rect) +{ + auto rect = a_rect; + rect.set_location(to_main_frame_position(a_rect.location())); + return rect; +} + +Gfx::IntPoint Frame::to_main_frame_position(const Gfx::IntPoint& a_position) +{ + auto position = a_position; + for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) { + if (ancestor->is_main_frame()) + break; + if (!ancestor->host_element()) + return {}; + if (!ancestor->host_element()->layout_node()) + return {}; + position.move_by(ancestor->host_element()->layout_node()->box_type_agnostic_position().to_type<int>()); + } + return position; +} + +void Frame::set_cursor_position(const DOM::Position& position) +{ + if (m_cursor_position == position) + return; + + if (m_cursor_position.node() && m_cursor_position.node()->layout_node()) + m_cursor_position.node()->layout_node()->set_needs_display(); + + m_cursor_position = position; + + if (m_cursor_position.node() && m_cursor_position.node()->layout_node()) + m_cursor_position.node()->layout_node()->set_needs_display(); + + dbgln("Cursor position: {}", m_cursor_position); +} + +String Frame::selected_text() const +{ + StringBuilder builder; + if (!m_document) + return {}; + auto* layout_root = m_document->layout_node(); + if (!layout_root) + return {}; + if (!layout_root->selection().is_valid()) + return {}; + + auto selection = layout_root->selection().normalized(); + + if (selection.start().layout_node == selection.end().layout_node) { + if (!is<Layout::TextNode>(*selection.start().layout_node)) + return ""; + return downcast<Layout::TextNode>(*selection.start().layout_node).text_for_rendering().substring(selection.start().index_in_node, selection.end().index_in_node - selection.start().index_in_node); + } + + // Start node + auto layout_node = selection.start().layout_node; + if (is<Layout::TextNode>(*layout_node)) { + auto& text = downcast<Layout::TextNode>(*layout_node).text_for_rendering(); + builder.append(text.substring(selection.start().index_in_node, text.length() - selection.start().index_in_node)); + } + + // Middle nodes + layout_node = layout_node->next_in_pre_order(); + while (layout_node && layout_node != selection.end().layout_node) { + if (is<Layout::TextNode>(*layout_node)) + builder.append(downcast<Layout::TextNode>(*layout_node).text_for_rendering()); + else if (is<Layout::BreakNode>(*layout_node) || is<Layout::BlockBox>(*layout_node)) + builder.append('\n'); + + layout_node = layout_node->next_in_pre_order(); + } + + // End node + ASSERT(layout_node == selection.end().layout_node); + if (is<Layout::TextNode>(*layout_node)) { + auto& text = downcast<Layout::TextNode>(*layout_node).text_for_rendering(); + builder.append(text.substring(0, selection.end().index_in_node)); + } + + return builder.to_string(); +} + +} diff --git a/Userland/Libraries/LibWeb/Page/Frame.h b/Userland/Libraries/LibWeb/Page/Frame.h new file mode 100644 index 0000000000..d6ba5eea8d --- /dev/null +++ b/Userland/Libraries/LibWeb/Page/Frame.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018-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 <AK/Function.h> +#include <AK/Noncopyable.h> +#include <AK/RefPtr.h> +#include <AK/WeakPtr.h> +#include <LibCore/Timer.h> +#include <LibGfx/Bitmap.h> +#include <LibGfx/Rect.h> +#include <LibGfx/Size.h> +#include <LibWeb/DOM/Position.h> +#include <LibWeb/Loader/FrameLoader.h> +#include <LibWeb/Page/EventHandler.h> +#include <LibWeb/TreeNode.h> + +namespace Web { + +class Frame : public TreeNode<Frame> { +public: + static NonnullRefPtr<Frame> create_subframe(DOM::Element& host_element, Frame& main_frame) { return adopt(*new Frame(host_element, main_frame)); } + static NonnullRefPtr<Frame> create(Page& page) { return adopt(*new Frame(page)); } + ~Frame(); + + bool is_main_frame() const { return this == &m_main_frame; } + bool is_focused_frame() const; + + const DOM::Document* document() const { return m_document; } + DOM::Document* document() { return m_document; } + + void set_document(DOM::Document*); + + Page* page() { return m_page; } + const Page* page() const { return m_page; } + + const Gfx::IntSize& size() const { return m_size; } + void set_size(const Gfx::IntSize&); + + void set_needs_display(const Gfx::IntRect&); + + void set_viewport_scroll_offset(const Gfx::IntPoint&); + Gfx::IntRect viewport_rect() const { return { m_viewport_scroll_offset, m_size }; } + + void did_scroll(Badge<InProcessWebView>); + + FrameLoader& loader() { return m_loader; } + const FrameLoader& loader() const { return m_loader; } + + EventHandler& event_handler() { return m_event_handler; } + const EventHandler& event_handler() const { return m_event_handler; } + + void scroll_to(const Gfx::IntPoint&); + void scroll_to_anchor(const String&); + + Frame& main_frame() { return m_main_frame; } + const Frame& main_frame() const { return m_main_frame; } + + DOM::Element* host_element() { return m_host_element; } + const DOM::Element* host_element() const { return m_host_element; } + + Gfx::IntPoint to_main_frame_position(const Gfx::IntPoint&); + Gfx::IntRect to_main_frame_rect(const Gfx::IntRect&); + + DOM::Position& cursor_position() { return m_cursor_position; } + const DOM::Position& cursor_position() const { return m_cursor_position; } + void set_cursor_position(const DOM::Position&); + + bool cursor_blink_state() const { return m_cursor_blink_state; } + + String selected_text() const; + + void did_edit(Badge<EditEventHandler>); + +private: + explicit Frame(DOM::Element& host_element, Frame& main_frame); + explicit Frame(Page&); + + void setup(); + + WeakPtr<Page> m_page; + Frame& m_main_frame; + + FrameLoader m_loader; + EventHandler m_event_handler; + + WeakPtr<DOM::Element> m_host_element; + RefPtr<DOM::Document> m_document; + Gfx::IntSize m_size; + Gfx::IntPoint m_viewport_scroll_offset; + + DOM::Position m_cursor_position; + RefPtr<Core::Timer> m_cursor_blink_timer; + bool m_cursor_blink_state { false }; +}; + +} diff --git a/Userland/Libraries/LibWeb/Page/Page.cpp b/Userland/Libraries/LibWeb/Page/Page.cpp new file mode 100644 index 0000000000..dfc9ada662 --- /dev/null +++ b/Userland/Libraries/LibWeb/Page/Page.cpp @@ -0,0 +1,95 @@ +/* + * 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 <LibWeb/InProcessWebView.h> +#include <LibWeb/Page/Frame.h> +#include <LibWeb/Page/Page.h> + +namespace Web { + +Page::Page(PageClient& client) + : m_client(client) +{ + m_main_frame = Frame::create(*this); +} + +Page::~Page() +{ +} + +Frame& Page::focused_frame() +{ + if (m_focused_frame) + return *m_focused_frame; + return main_frame(); +} + +void Page::set_focused_frame(Badge<EventHandler>, Frame& frame) +{ + m_focused_frame = frame.make_weak_ptr(); +} + +void Page::load(const URL& url) +{ + main_frame().loader().load(url, FrameLoader::Type::Navigation); +} + +void Page::load(const LoadRequest& request) +{ + main_frame().loader().load(request, FrameLoader::Type::Navigation); +} + +void Page::load_html(const StringView& html, const URL& url) +{ + main_frame().loader().load_html(html, url); +} + +Gfx::Palette Page::palette() const +{ + return m_client.palette(); +} + +bool Page::handle_mouseup(const Gfx::IntPoint& position, unsigned button, unsigned modifiers) +{ + return main_frame().event_handler().handle_mouseup(position, button, modifiers); +} + +bool Page::handle_mousedown(const Gfx::IntPoint& position, unsigned button, unsigned modifiers) +{ + return main_frame().event_handler().handle_mousedown(position, button, modifiers); +} + +bool Page::handle_mousemove(const Gfx::IntPoint& position, unsigned buttons, unsigned modifiers) +{ + return main_frame().event_handler().handle_mousemove(position, buttons, modifiers); +} + +bool Page::handle_keydown(KeyCode key, unsigned modifiers, u32 code_point) +{ + return focused_frame().event_handler().handle_keydown(key, modifiers, code_point); +} + +} diff --git a/Userland/Libraries/LibWeb/Page/Page.h b/Userland/Libraries/LibWeb/Page/Page.h new file mode 100644 index 0000000000..5d7325d283 --- /dev/null +++ b/Userland/Libraries/LibWeb/Page/Page.h @@ -0,0 +1,107 @@ +/* + * 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 <AK/Noncopyable.h> +#include <AK/OwnPtr.h> +#include <AK/RefPtr.h> +#include <AK/URL.h> +#include <Kernel/API/KeyCode.h> +#include <LibGUI/Window.h> +#include <LibGfx/Forward.h> +#include <LibGfx/Palette.h> +#include <LibWeb/Forward.h> + +namespace Web { + +class PageClient; + +class Page : public Weakable<Page> { + AK_MAKE_NONCOPYABLE(Page); + AK_MAKE_NONMOVABLE(Page); + +public: + explicit Page(PageClient&); + ~Page(); + + PageClient& client() { return m_client; } + const PageClient& client() const { return m_client; } + + Web::Frame& main_frame() { return *m_main_frame; } + const Web::Frame& main_frame() const { return *m_main_frame; } + + Web::Frame& focused_frame(); + const Web::Frame& focused_frame() const { return const_cast<Page*>(this)->focused_frame(); } + + void set_focused_frame(Badge<EventHandler>, Frame&); + + void load(const URL&); + void load(const LoadRequest&); + + void load_html(const StringView&, const URL&); + + bool handle_mouseup(const Gfx::IntPoint&, unsigned button, unsigned modifiers); + bool handle_mousedown(const Gfx::IntPoint&, unsigned button, unsigned modifiers); + bool handle_mousemove(const Gfx::IntPoint&, unsigned buttons, unsigned modifiers); + + bool handle_keydown(KeyCode, unsigned modifiers, u32 code_point); + + Gfx::Palette palette() const; + +private: + PageClient& m_client; + + RefPtr<Frame> m_main_frame; + WeakPtr<Frame> m_focused_frame; +}; + +class PageClient { +public: + virtual Gfx::Palette palette() const = 0; + virtual void page_did_set_document_in_main_frame(DOM::Document*) { } + virtual void page_did_change_title(const String&) { } + virtual void page_did_start_loading(const URL&) { } + virtual void page_did_finish_loading(const URL&) { } + virtual void page_did_change_selection() { } + virtual void page_did_request_cursor_change(Gfx::StandardCursor) { } + virtual void page_did_request_context_menu(const Gfx::IntPoint&) { } + virtual void page_did_request_link_context_menu(const Gfx::IntPoint&, const URL&, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers) { } + virtual void page_did_request_image_context_menu(const Gfx::IntPoint&, const URL&, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers, const Gfx::Bitmap*) { } + virtual void page_did_click_link(const URL&, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers) { } + virtual void page_did_middle_click_link(const URL&, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers) { } + virtual void page_did_enter_tooltip_area(const Gfx::IntPoint&, const String&) { } + virtual void page_did_leave_tooltip_area() { } + virtual void page_did_hover_link(const URL&) { } + virtual void page_did_unhover_link() { } + virtual void page_did_invalidate(const Gfx::IntRect&) { } + virtual void page_did_change_favicon(const Gfx::Bitmap&) { } + virtual void page_did_layout() { } + virtual void page_did_request_scroll_into_view(const Gfx::IntRect&) { } + virtual void page_did_request_alert(const String&) { } +}; + +} diff --git a/Userland/Libraries/LibWeb/Painting/BorderPainting.cpp b/Userland/Libraries/LibWeb/Painting/BorderPainting.cpp new file mode 100644 index 0000000000..3c5cd8831f --- /dev/null +++ b/Userland/Libraries/LibWeb/Painting/BorderPainting.cpp @@ -0,0 +1,162 @@ +/* + * 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 <LibGfx/Painter.h> +#include <LibWeb/Painting/BorderPainting.h> +#include <LibWeb/Painting/PaintContext.h> + +namespace Web::Painting { + +void paint_border(PaintContext& context, BorderEdge edge, const Gfx::FloatRect& rect, const CSS::ComputedValues& style) +{ + const auto& border_data = [&] { + switch (edge) { + case BorderEdge::Top: + return style.border_top(); + case BorderEdge::Right: + return style.border_right(); + case BorderEdge::Bottom: + return style.border_bottom(); + default: // BorderEdge::Left: + return style.border_left(); + } + }(); + + float width = border_data.width; + if (width <= 0) + return; + + auto color = border_data.color; + auto border_style = border_data.line_style; + int int_width = max((int)width, 1); + + struct Points { + Gfx::FloatPoint p1; + Gfx::FloatPoint p2; + }; + + auto points_for_edge = [](BorderEdge edge, const Gfx::FloatRect& rect) -> Points { + switch (edge) { + case BorderEdge::Top: + return { rect.top_left(), rect.top_right() }; + case BorderEdge::Right: + return { rect.top_right(), rect.bottom_right() }; + case BorderEdge::Bottom: + return { rect.bottom_left(), rect.bottom_right() }; + default: // Edge::Left + return { rect.top_left(), rect.bottom_left() }; + } + }; + + auto [p1, p2] = points_for_edge(edge, rect); + + if (border_style == CSS::LineStyle::Inset) { + auto top_left_color = Color::from_rgb(0x5a5a5a); + auto bottom_right_color = Color::from_rgb(0x888888); + color = (edge == BorderEdge::Left || edge == BorderEdge::Top) ? top_left_color : bottom_right_color; + } else if (border_style == CSS::LineStyle::Outset) { + auto top_left_color = Color::from_rgb(0x888888); + auto bottom_right_color = Color::from_rgb(0x5a5a5a); + color = (edge == BorderEdge::Left || edge == BorderEdge::Top) ? top_left_color : bottom_right_color; + } + + auto gfx_line_style = Gfx::Painter::LineStyle::Solid; + if (border_style == CSS::LineStyle::Dotted) + gfx_line_style = Gfx::Painter::LineStyle::Dotted; + if (border_style == CSS::LineStyle::Dashed) + gfx_line_style = Gfx::Painter::LineStyle::Dashed; + + if (gfx_line_style != Gfx::Painter::LineStyle::Solid) { + switch (edge) { + case BorderEdge::Top: + p1.move_by(int_width / 2, int_width / 2); + p2.move_by(-int_width / 2, int_width / 2); + break; + case BorderEdge::Right: + p1.move_by(-int_width / 2, int_width / 2); + p2.move_by(-int_width / 2, -int_width / 2); + break; + case BorderEdge::Bottom: + p1.move_by(int_width / 2, -int_width / 2); + p2.move_by(-int_width / 2, -int_width / 2); + break; + case BorderEdge::Left: + p1.move_by(int_width / 2, int_width / 2); + p2.move_by(int_width / 2, -int_width / 2); + break; + } + context.painter().draw_line({ (int)p1.x(), (int)p1.y() }, { (int)p2.x(), (int)p2.y() }, color, int_width, gfx_line_style); + return; + } + + auto draw_line = [&](auto& p1, auto& p2) { + context.painter().draw_line({ (int)p1.x(), (int)p1.y() }, { (int)p2.x(), (int)p2.y() }, color, 1, gfx_line_style); + }; + + float p1_step = 0; + float p2_step = 0; + + switch (edge) { + case BorderEdge::Top: + p1_step = style.border_left().width / (float)int_width; + p2_step = style.border_right().width / (float)int_width; + for (int i = 0; i < int_width; ++i) { + draw_line(p1, p2); + p1.move_by(p1_step, 1); + p2.move_by(-p2_step, 1); + } + break; + case BorderEdge::Right: + p1_step = style.border_top().width / (float)int_width; + p2_step = style.border_bottom().width / (float)int_width; + for (int i = int_width - 1; i >= 0; --i) { + draw_line(p1, p2); + p1.move_by(-1, p1_step); + p2.move_by(-1, -p2_step); + } + break; + case BorderEdge::Bottom: + p1_step = style.border_left().width / (float)int_width; + p2_step = style.border_right().width / (float)int_width; + for (int i = int_width - 1; i >= 0; --i) { + draw_line(p1, p2); + p1.move_by(p1_step, -1); + p2.move_by(-p2_step, -1); + } + break; + case BorderEdge::Left: + p1_step = style.border_top().width / (float)int_width; + p2_step = style.border_bottom().width / (float)int_width; + for (int i = 0; i < int_width; ++i) { + draw_line(p1, p2); + p1.move_by(1, p1_step); + p2.move_by(1, -p2_step); + } + break; + } +} + +} diff --git a/Userland/Libraries/LibWeb/Painting/BorderPainting.h b/Userland/Libraries/LibWeb/Painting/BorderPainting.h new file mode 100644 index 0000000000..47633eb980 --- /dev/null +++ b/Userland/Libraries/LibWeb/Painting/BorderPainting.h @@ -0,0 +1,42 @@ +/* + * 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 <LibGfx/Forward.h> +#include <LibWeb/CSS/ComputedValues.h> + +namespace Web::Painting { + +enum class BorderEdge { + Top, + Right, + Bottom, + Left, +}; +void paint_border(PaintContext&, BorderEdge, const Gfx::FloatRect&, const CSS::ComputedValues&); + +} diff --git a/Userland/Libraries/LibWeb/Painting/PaintContext.h b/Userland/Libraries/LibWeb/Painting/PaintContext.h new file mode 100644 index 0000000000..f6ba50eb87 --- /dev/null +++ b/Userland/Libraries/LibWeb/Painting/PaintContext.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018-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 <LibGfx/Forward.h> +#include <LibGfx/Palette.h> +#include <LibGfx/Rect.h> +#include <LibWeb/SVG/SVGContext.h> + +namespace Web { + +class PaintContext { +public: + explicit PaintContext(Gfx::Painter& painter, const Palette& palette, const Gfx::IntPoint& scroll_offset) + : m_painter(painter) + , m_palette(palette) + , m_scroll_offset(scroll_offset) + { + } + + Gfx::Painter& painter() const { return m_painter; } + const Palette& palette() const { return m_palette; } + + bool has_svg_context() const { return m_svg_context.has_value(); } + SVGContext& svg_context() { return m_svg_context.value(); } + void set_svg_context(SVGContext context) { m_svg_context = context; } + + bool should_show_line_box_borders() const { return m_should_show_line_box_borders; } + void set_should_show_line_box_borders(bool value) { m_should_show_line_box_borders = value; } + + Gfx::IntRect viewport_rect() const { return m_viewport_rect; } + void set_viewport_rect(const Gfx::IntRect& rect) { m_viewport_rect = rect; } + + const Gfx::IntPoint& scroll_offset() const { return m_scroll_offset; } + + bool has_focus() const { return m_focus; } + void set_has_focus(bool focus) { m_focus = focus; } + +private: + Gfx::Painter& m_painter; + Palette m_palette; + Optional<SVGContext> m_svg_context; + Gfx::IntRect m_viewport_rect; + Gfx::IntPoint m_scroll_offset; + bool m_should_show_line_box_borders { false }; + bool m_focus { false }; +}; + +} diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp new file mode 100644 index 0000000000..7524f4fff9 --- /dev/null +++ b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp @@ -0,0 +1,95 @@ +/* + * 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/QuickSort.h> +#include <AK/StringBuilder.h> +#include <LibWeb/DOM/Node.h> +#include <LibWeb/Layout/Box.h> +#include <LibWeb/Layout/InitialContainingBlockBox.h> +#include <LibWeb/Painting/StackingContext.h> + +namespace Web::Layout { + +StackingContext::StackingContext(Box& box, StackingContext* parent) + : m_box(box) + , m_parent(parent) +{ + ASSERT(m_parent != this); + if (m_parent) { + m_parent->m_children.append(this); + + // FIXME: Don't sort on every append.. + quick_sort(m_parent->m_children, [](auto& a, auto& b) { + return a->m_box.computed_values().z_index().value_or(0) < b->m_box.computed_values().z_index().value_or(0); + }); + } +} + +void StackingContext::paint(PaintContext& context, PaintPhase phase) +{ + if (!is<InitialContainingBlockBox>(m_box)) { + m_box.paint(context, phase); + } else { + // NOTE: InitialContainingBlockBox::paint() merely calls StackingContext::paint() + // so we call its base class instead. + downcast<InitialContainingBlockBox>(m_box).BlockBox::paint(context, phase); + } + for (auto* child : m_children) { + child->paint(context, phase); + } +} + +HitTestResult StackingContext::hit_test(const Gfx::IntPoint& position, HitTestType type) const +{ + HitTestResult result; + if (!is<InitialContainingBlockBox>(m_box)) { + result = m_box.hit_test(position, type); + } else { + // NOTE: InitialContainingBlockBox::hit_test() merely calls StackingContext::hit_test() + // so we call its base class instead. + result = downcast<InitialContainingBlockBox>(m_box).BlockBox::hit_test(position, type); + } + + for (auto* child : m_children) { + auto result_here = child->hit_test(position, type); + if (result_here.layout_node) + result = result_here; + } + return result; +} + +void StackingContext::dump(int indent) const +{ + StringBuilder builder; + for (int i = 0; i < indent; ++i) + builder.append(' '); + builder.appendff("SC for {}({}) {} [children: {}]", m_box.class_name(), m_box.dom_node() ? m_box.dom_node()->node_name().characters() : "(anonymous)", m_box.absolute_rect().to_string().characters(), m_children.size()); + dbgln("{}", builder.string_view()); + for (auto& child : m_children) + child->dump(indent + 1); +} + +} diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.h b/Userland/Libraries/LibWeb/Painting/StackingContext.h new file mode 100644 index 0000000000..b0b212ed2d --- /dev/null +++ b/Userland/Libraries/LibWeb/Painting/StackingContext.h @@ -0,0 +1,52 @@ +/* + * 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 <AK/Vector.h> +#include <LibWeb/Layout/Node.h> + +namespace Web::Layout { + +class StackingContext { +public: + StackingContext(Box&, StackingContext* parent); + + StackingContext* parent() { return m_parent; } + const StackingContext* parent() const { return m_parent; } + + void paint(PaintContext&, PaintPhase); + HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const; + + void dump(int indent = 0) const; + +private: + Box& m_box; + StackingContext* const m_parent { nullptr }; + Vector<StackingContext*> m_children; +}; + +} diff --git a/Userland/Libraries/LibWeb/QualifiedName.h b/Userland/Libraries/LibWeb/QualifiedName.h new file mode 100644 index 0000000000..08103b507d --- /dev/null +++ b/Userland/Libraries/LibWeb/QualifiedName.h @@ -0,0 +1,52 @@ +/* + * 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 <AK/FlyString.h> + +namespace Web { + +class QualifiedName { +public: + QualifiedName(const FlyString& local_name, const FlyString& prefix, const FlyString& namespace_) + : m_local_name(local_name) + , m_prefix(prefix) + , m_namespace(namespace_) + { + } + + const FlyString& local_name() const { return m_local_name; } + const FlyString& prefix() const { return m_prefix; } + const FlyString& namespace_() const { return m_namespace; } + +private: + FlyString m_local_name; + FlyString m_prefix; + FlyString m_namespace; +}; + +} diff --git a/Userland/Libraries/LibWeb/SVG/SVGContext.h b/Userland/Libraries/LibWeb/SVG/SVGContext.h new file mode 100644 index 0000000000..ec7fe72eff --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGContext.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 + +namespace Web { + +class SVGContext { +public: + SVGContext() + { + m_states.append(State()); + } + + const Gfx::Color& fill_color() const { return state().fill_color; } + const Gfx::Color& stroke_color() const { return state().stroke_color; } + float stroke_width() const { return state().stroke_width; } + + void set_fill_color(Gfx::Color color) { state().fill_color = color; } + void set_stroke_color(Gfx::Color color) { state().stroke_color = color; } + void set_stroke_width(float width) { state().stroke_width = width; } + + void save() { m_states.append(m_states.last()); } + void restore() { m_states.take_last(); } + +private: + struct State { + Gfx::Color fill_color { Gfx::Color::Black }; + Gfx::Color stroke_color { Gfx::Color::Transparent }; + float stroke_width { 1.0 }; + }; + + const State& state() const { return m_states.last(); } + State& state() { return m_states.last(); } + + Vector<State> m_states; +}; + +} diff --git a/Userland/Libraries/LibWeb/SVG/SVGElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGElement.cpp new file mode 100644 index 0000000000..828e9b0e99 --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGElement.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibWeb/SVG/SVGElement.h> + +namespace Web::SVG { + +SVGElement::SVGElement(DOM::Document& document, const QualifiedName& qualified_name) + : Element(document, qualified_name) +{ +} + +} diff --git a/Userland/Libraries/LibWeb/SVG/SVGElement.h b/Userland/Libraries/LibWeb/SVG/SVGElement.h new file mode 100644 index 0000000000..99041728f8 --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGElement.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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/DOM/Element.h> + +namespace Web::SVG { + +class SVGElement : public DOM::Element { +public: + using WrapperType = Bindings::SVGElementWrapper; + +protected: + SVGElement(DOM::Document&, const QualifiedName& qualified_name); +}; + +} diff --git a/Userland/Libraries/LibWeb/SVG/SVGElement.idl b/Userland/Libraries/LibWeb/SVG/SVGElement.idl new file mode 100644 index 0000000000..1eb7f7f18c --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGElement.idl @@ -0,0 +1,3 @@ +interface SVGElement : Element { + +}; diff --git a/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.cpp new file mode 100644 index 0000000000..c3e8f386fd --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibWeb/SVG/SVGGeometryElement.h> + +namespace Web::SVG { + +SVGGeometryElement::SVGGeometryElement(DOM::Document& document, const QualifiedName& qualified_name) + : SVGGraphicsElement(document, qualified_name) +{ +} + +} diff --git a/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.h b/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.h new file mode 100644 index 0000000000..91c9e92288 --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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/SVG/SVGGraphicsElement.h> + +namespace Web::SVG { + +class SVGGeometryElement : public SVGGraphicsElement { +public: + using WrapperType = Bindings::SVGGeometryElementWrapper; + +protected: + SVGGeometryElement(DOM::Document& document, const QualifiedName& qualified_name); +}; + +} diff --git a/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.idl b/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.idl new file mode 100644 index 0000000000..bd64356c71 --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.idl @@ -0,0 +1,3 @@ +interface SVGGeometryElement : SVGGraphicsElement { + +}; diff --git a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp new file mode 100644 index 0000000000..2fca526027 --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibWeb/SVG/SVGGraphicsElement.h> + +namespace Web::SVG { + +SVGGraphicsElement::SVGGraphicsElement(DOM::Document& document, const QualifiedName& qualified_name) + : SVGElement(document, qualified_name) +{ +} + +void SVGGraphicsElement::parse_attribute(const FlyString& name, const String& value) +{ + SVGElement::parse_attribute(name, value); + + if (name == "fill") { + m_fill_color = Gfx::Color::from_string(value).value_or(Color::Transparent); + } else if (name == "stroke") { + m_stroke_color = Gfx::Color::from_string(value).value_or(Color::Transparent); + } else if (name == "stroke-width") { + auto result = value.to_int(); + if (result.has_value()) + m_stroke_width = result.value(); + } +} + +} diff --git a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h new file mode 100644 index 0000000000..ec8735a70b --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibGfx/Path.h> +#include <LibWeb/DOM/Node.h> +#include <LibWeb/SVG/SVGElement.h> +#include <LibWeb/SVG/TagNames.h> + +namespace Web::SVG { + +class SVGGraphicsElement : public SVGElement { +public: + using WrapperType = Bindings::SVGGraphicsElementWrapper; + + SVGGraphicsElement(DOM::Document&, const QualifiedName& qualified_name); + + virtual void parse_attribute(const FlyString& name, const String& value) override; + + const Optional<Gfx::Color>& fill_color() const { return m_fill_color; } + const Optional<Gfx::Color>& stroke_color() const { return m_stroke_color; } + const Optional<float>& stroke_width() const { return m_stroke_width; } + +protected: + Optional<Gfx::Color> m_fill_color; + Optional<Gfx::Color> m_stroke_color; + Optional<float> m_stroke_width; +}; + +} diff --git a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.idl b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.idl new file mode 100644 index 0000000000..9636176dd0 --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.idl @@ -0,0 +1,3 @@ +interface SVGGraphicsElement : SVGElement { + +}; diff --git a/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp new file mode 100644 index 0000000000..5a3c01fc4c --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp @@ -0,0 +1,659 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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/StringBuilder.h> +#include <LibGfx/Painter.h> +#include <LibGfx/Path.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/Layout/SVGPathBox.h> +#include <LibWeb/SVG/SVGPathElement.h> +#include <ctype.h> + +//#define PATH_DEBUG + +namespace Web::SVG { + +#ifdef PATH_DEBUG +static void print_instruction(const PathInstruction& instruction) +{ + auto& data = instruction.data; + + switch (instruction.type) { + case PathInstructionType::Move: + dbg() << "Move (absolute=" << instruction.absolute << ")"; + for (size_t i = 0; i < data.size(); i += 2) + dbg() << " x=" << data[i] << ", y=" << data[i + 1]; + break; + case PathInstructionType::ClosePath: + dbg() << "ClosePath (absolute=" << instruction.absolute << ")"; + break; + case PathInstructionType::Line: + dbg() << "Line (absolute=" << instruction.absolute << ")"; + for (size_t i = 0; i < data.size(); i += 2) + dbg() << " x=" << data[i] << ", y=" << data[i + 1]; + break; + case PathInstructionType::HorizontalLine: + dbg() << "HorizontalLine (absolute=" << instruction.absolute << ")"; + for (size_t i = 0; i < data.size(); ++i) + dbg() << " x=" << data[i]; + break; + case PathInstructionType::VerticalLine: + dbg() << "VerticalLine (absolute=" << instruction.absolute << ")"; + for (size_t i = 0; i < data.size(); ++i) + dbg() << " y=" << data[i]; + break; + case PathInstructionType::Curve: + dbg() << "Curve (absolute=" << instruction.absolute << ")"; + for (size_t i = 0; i < data.size(); i += 6) + dbg() << " (x1=" << data[i] << ", y1=" << data[i + 1] << "), (x2=" << data[i + 2] << ", y2=" << data[i + 3] << "), (x=" << data[i + 4] << ", y=" << data[i + 5] << ")"; + break; + case PathInstructionType::SmoothCurve: + dbg() << "SmoothCurve (absolute=" << instruction.absolute << ")"; + for (size_t i = 0; i < data.size(); i += 4) + dbg() << " (x2=" << data[i] << ", y2=" << data[i + 1] << "), (x=" << data[i + 2] << ", y=" << data[i + 3] << ")"; + break; + case PathInstructionType::QuadraticBezierCurve: + dbg() << "QuadraticBezierCurve (absolute=" << instruction.absolute << ")"; + for (size_t i = 0; i < data.size(); i += 4) + dbg() << " (x1=" << data[i] << ", y1=" << data[i + 1] << "), (x=" << data[i + 2] << ", y=" << data[i + 3] << ")"; + break; + case PathInstructionType::SmoothQuadraticBezierCurve: + dbg() << "SmoothQuadraticBezierCurve (absolute=" << instruction.absolute << ")"; + for (size_t i = 0; i < data.size(); i += 2) + dbg() << " x=" << data[i] << ", y=" << data[i + 1]; + break; + case PathInstructionType::EllipticalArc: + dbg() << "EllipticalArc (absolute=" << instruction.absolute << ")"; + for (size_t i = 0; i < data.size(); i += 7) + dbg() << " (rx=" << data[i] << ", ry=" << data[i + 1] << ") x-axis-rotation=" << data[i + 2] << ", large-arc-flag=" << data[i + 3] << ", sweep-flag=" << data[i + 4] << ", (x=" << data[i + 5] << ", y=" << data[i + 6] << ")"; + break; + case PathInstructionType::Invalid: + dbgln("Invalid"); + break; + } +} +#endif + +PathDataParser::PathDataParser(const String& source) + : m_source(source) +{ +} + +Vector<PathInstruction> PathDataParser::parse() +{ + parse_whitespace(); + while (!done()) + parse_drawto(); + if (!m_instructions.is_empty() && m_instructions[0].type != PathInstructionType::Move) + ASSERT_NOT_REACHED(); + return m_instructions; +} + +void PathDataParser::parse_drawto() +{ + if (match('M') || match('m')) { + parse_moveto(); + } else if (match('Z') || match('z')) { + parse_closepath(); + } else if (match('L') || match('l')) { + parse_lineto(); + } else if (match('H') || match('h')) { + parse_horizontal_lineto(); + } else if (match('V') || match('v')) { + parse_vertical_lineto(); + } else if (match('C') || match('c')) { + parse_curveto(); + } else if (match('S') || match('s')) { + parse_smooth_curveto(); + } else if (match('Q') || match('q')) { + parse_quadratic_bezier_curveto(); + } else if (match('T') || match('t')) { + parse_smooth_quadratic_bezier_curveto(); + } else if (match('A') || match('a')) { + parse_elliptical_arc(); + } else { + dbg() << "PathDataParser::parse_drawto failed to match: '" << ch() << "'"; + TODO(); + } +} + +void PathDataParser::parse_moveto() +{ + bool absolute = consume() == 'M'; + parse_whitespace(); + for (auto& pair : parse_coordinate_pair_sequence()) + m_instructions.append({ PathInstructionType::Move, absolute, pair }); +} + +void PathDataParser::parse_closepath() +{ + bool absolute = consume() == 'Z'; + parse_whitespace(); + m_instructions.append({ PathInstructionType::ClosePath, absolute, {} }); +} + +void PathDataParser::parse_lineto() +{ + bool absolute = consume() == 'L'; + parse_whitespace(); + for (auto& pair : parse_coordinate_pair_sequence()) + m_instructions.append({ PathInstructionType::Line, absolute, pair }); +} + +void PathDataParser::parse_horizontal_lineto() +{ + bool absolute = consume() == 'H'; + parse_whitespace(); + m_instructions.append({ PathInstructionType::HorizontalLine, absolute, parse_coordinate_sequence() }); +} + +void PathDataParser::parse_vertical_lineto() +{ + bool absolute = consume() == 'V'; + parse_whitespace(); + m_instructions.append({ PathInstructionType::VerticalLine, absolute, parse_coordinate_sequence() }); +} + +void PathDataParser::parse_curveto() +{ + bool absolute = consume() == 'C'; + parse_whitespace(); + + while (true) { + m_instructions.append({ PathInstructionType::Curve, absolute, parse_coordinate_pair_triplet() }); + if (match_comma_whitespace()) + parse_comma_whitespace(); + if (!match_coordinate()) + break; + } +} + +void PathDataParser::parse_smooth_curveto() +{ + bool absolute = consume() == 'S'; + parse_whitespace(); + + while (true) { + m_instructions.append({ PathInstructionType::SmoothCurve, absolute, parse_coordinate_pair_double() }); + if (match_comma_whitespace()) + parse_comma_whitespace(); + if (!match_coordinate()) + break; + } +} + +void PathDataParser::parse_quadratic_bezier_curveto() +{ + bool absolute = consume() == 'Q'; + parse_whitespace(); + + while (true) { + m_instructions.append({ PathInstructionType::QuadraticBezierCurve, absolute, parse_coordinate_pair_double() }); + if (match_comma_whitespace()) + parse_comma_whitespace(); + if (!match_coordinate()) + break; + } +} + +void PathDataParser::parse_smooth_quadratic_bezier_curveto() +{ + bool absolute = consume() == 'T'; + parse_whitespace(); + + while (true) { + m_instructions.append({ PathInstructionType::SmoothQuadraticBezierCurve, absolute, parse_coordinate_pair() }); + if (match_comma_whitespace()) + parse_comma_whitespace(); + if (!match_coordinate()) + break; + } +} + +void PathDataParser::parse_elliptical_arc() +{ + bool absolute = consume() == 'A'; + parse_whitespace(); + + while (true) { + m_instructions.append({ PathInstructionType::EllipticalArc, absolute, parse_elliptical_arg_argument() }); + if (match_comma_whitespace()) + parse_comma_whitespace(); + if (!match_coordinate()) + break; + } +} + +float PathDataParser::parse_coordinate() +{ + return parse_sign() * parse_number(); +} + +Vector<float> PathDataParser::parse_coordinate_pair() +{ + Vector<float> coordinates; + coordinates.append(parse_coordinate()); + if (match_comma_whitespace()) + parse_comma_whitespace(); + coordinates.append(parse_coordinate()); + return coordinates; +} + +Vector<float> PathDataParser::parse_coordinate_sequence() +{ + Vector<float> sequence; + while (true) { + sequence.append(parse_coordinate()); + if (match_comma_whitespace()) + parse_comma_whitespace(); + if (!match_comma_whitespace() && !match_coordinate()) + break; + } + return sequence; +} + +Vector<Vector<float>> PathDataParser::parse_coordinate_pair_sequence() +{ + Vector<Vector<float>> sequence; + while (true) { + sequence.append(parse_coordinate_pair()); + if (match_comma_whitespace()) + parse_comma_whitespace(); + if (!match_comma_whitespace() && !match_coordinate()) + break; + } + return sequence; +} + +Vector<float> PathDataParser::parse_coordinate_pair_double() +{ + Vector<float> coordinates; + coordinates.append(parse_coordinate_pair()); + if (match_comma_whitespace()) + parse_comma_whitespace(); + coordinates.append(parse_coordinate_pair()); + return coordinates; +} + +Vector<float> PathDataParser::parse_coordinate_pair_triplet() +{ + Vector<float> coordinates; + coordinates.append(parse_coordinate_pair()); + if (match_comma_whitespace()) + parse_comma_whitespace(); + coordinates.append(parse_coordinate_pair()); + if (match_comma_whitespace()) + parse_comma_whitespace(); + coordinates.append(parse_coordinate_pair()); + return coordinates; +} + +Vector<float> PathDataParser::parse_elliptical_arg_argument() +{ + Vector<float> numbers; + numbers.append(parse_number()); + if (match_comma_whitespace()) + parse_comma_whitespace(); + numbers.append(parse_number()); + if (match_comma_whitespace()) + parse_comma_whitespace(); + numbers.append(parse_number()); + parse_comma_whitespace(); + numbers.append(parse_flag()); + if (match_comma_whitespace()) + parse_comma_whitespace(); + numbers.append(parse_flag()); + if (match_comma_whitespace()) + parse_comma_whitespace(); + numbers.append(parse_coordinate_pair()); + + return numbers; +} + +void PathDataParser::parse_whitespace(bool must_match_once) +{ + bool matched = false; + while (!done() && match_whitespace()) { + consume(); + matched = true; + } + + ASSERT(!must_match_once || matched); +} + +void PathDataParser::parse_comma_whitespace() +{ + if (match(',')) { + consume(); + parse_whitespace(); + } else { + parse_whitespace(1); + if (match(',')) + consume(); + parse_whitespace(); + } +} + +float PathDataParser::parse_fractional_constant() +{ + StringBuilder builder; + bool floating_point = false; + + while (!done() && isdigit(ch())) + builder.append(consume()); + + if (match('.')) { + floating_point = true; + builder.append('.'); + consume(); + while (!done() && isdigit(ch())) + builder.append(consume()); + } else { + ASSERT(builder.length() > 0); + } + + if (floating_point) + return strtof(builder.to_string().characters(), nullptr); + return builder.to_string().to_int().value(); +} + +float PathDataParser::parse_number() +{ + auto number = parse_fractional_constant(); + if (match('e') || match('E')) + TODO(); + return number; +} + +float PathDataParser::parse_flag() +{ + if (!match('0') && !match('1')) + ASSERT_NOT_REACHED(); + return consume() - '0'; +} + +int PathDataParser::parse_sign() +{ + if (match('-')) { + consume(); + return -1; + } + if (match('+')) + consume(); + return 1; +} + +bool PathDataParser::match_whitespace() const +{ + if (done()) + return false; + char c = ch(); + return c == 0x9 || c == 0x20 || c == 0xa || c == 0xc || c == 0xd; +} + +bool PathDataParser::match_comma_whitespace() const +{ + return match_whitespace() || match(','); +} + +bool PathDataParser::match_coordinate() const +{ + return !done() && (isdigit(ch()) || ch() == '-' || ch() == '+' || ch() == '.'); +} + +SVGPathElement::SVGPathElement(DOM::Document& document, const QualifiedName& qualified_name) + : SVGGeometryElement(document, qualified_name) +{ +} + +RefPtr<Layout::Node> SVGPathElement::create_layout_node() +{ + auto style = document().style_resolver().resolve_style(*this); + if (style->display() == CSS::Display::None) + return nullptr; + return adopt(*new Layout::SVGPathBox(document(), *this, move(style))); +} + +void SVGPathElement::parse_attribute(const FlyString& name, const String& value) +{ + SVGGeometryElement::parse_attribute(name, value); + + if (name == "d") + m_instructions = PathDataParser(value).parse(); +} + +Gfx::Path& SVGPathElement::get_path() +{ + if (m_path.has_value()) + return m_path.value(); + + Gfx::Path path; + + for (auto& instruction : m_instructions) { + auto& absolute = instruction.absolute; + auto& data = instruction.data; + +#ifdef PATH_DEBUG + print_instruction(instruction); +#endif + + bool clear_last_control_point = true; + + switch (instruction.type) { + case PathInstructionType::Move: { + Gfx::FloatPoint point = { data[0], data[1] }; + if (absolute) { + path.move_to(point); + } else { + ASSERT(!path.segments().is_empty()); + path.move_to(point + path.segments().last().point()); + } + break; + } + case PathInstructionType::ClosePath: + path.close(); + break; + case PathInstructionType::Line: { + Gfx::FloatPoint point = { data[0], data[1] }; + if (absolute) { + path.line_to(point); + } else { + ASSERT(!path.segments().is_empty()); + path.line_to(point + path.segments().last().point()); + } + break; + } + case PathInstructionType::HorizontalLine: { + ASSERT(!path.segments().is_empty()); + auto last_point = path.segments().last().point(); + if (absolute) { + path.line_to(Gfx::FloatPoint { data[0], last_point.y() }); + } else { + path.line_to(Gfx::FloatPoint { data[0] + last_point.x(), last_point.y() }); + } + break; + } + case PathInstructionType::VerticalLine: { + ASSERT(!path.segments().is_empty()); + auto last_point = path.segments().last().point(); + if (absolute) { + path.line_to(Gfx::FloatPoint { last_point.x(), data[0] }); + } else { + path.line_to(Gfx::FloatPoint { last_point.x(), data[0] + last_point.y() }); + } + break; + } + case PathInstructionType::EllipticalArc: { + double rx = data[0]; + double ry = data[1]; + double x_axis_rotation = data[2] * M_DEG2RAD; + double large_arc_flag = data[3]; + double sweep_flag = data[4]; + + double x_axis_rotation_c = cos(x_axis_rotation); + double x_axis_rotation_s = sin(x_axis_rotation); + + auto& last_point = path.segments().last().point(); + + Gfx::FloatPoint next_point; + + if (absolute) { + next_point = { data[5], data[6] }; + } else { + next_point = { data[5] + last_point.x(), data[6] + last_point.y() }; + } + + // Step 1 of out-of-range radii correction + if (rx == 0.0 || ry == 0.0) { + path.line_to(next_point); + break; + } + + // Step 2 of out-of-range radii correction + if (rx < 0) + rx *= -1.0; + if (ry < 0) + ry *= -1.0; + + // Find (cx, cy), theta_1, theta_delta + // Step 1: Compute (x1', y1') + auto x_avg = (last_point.x() - next_point.x()) / 2.0f; + auto y_avg = (last_point.y() - next_point.y()) / 2.0f; + auto x1p = x_axis_rotation_c * x_avg + x_axis_rotation_s * y_avg; + auto y1p = -x_axis_rotation_s * x_avg + x_axis_rotation_c * y_avg; + + // Step 2: Compute (cx', cy') + double x1p_sq = pow(x1p, 2.0); + double y1p_sq = pow(y1p, 2.0); + double rx_sq = pow(rx, 2.0); + double ry_sq = pow(ry, 2.0); + + // Step 3 of out-of-range radii correction + double lambda = x1p_sq / rx_sq + y1p_sq / ry_sq; + double multiplier; + + if (lambda > 1.0) { + auto lambda_sqrt = sqrt(lambda); + rx *= lambda_sqrt; + ry *= lambda_sqrt; + multiplier = 0.0; + } else { + double numerator = rx_sq * ry_sq - rx_sq * y1p_sq - ry_sq * x1p_sq; + double denominator = rx_sq * y1p_sq + ry_sq * x1p_sq; + multiplier = sqrt(numerator / denominator); + } + + if (large_arc_flag == sweep_flag) + multiplier *= -1.0; + + double cxp = multiplier * rx * y1p / ry; + double cyp = multiplier * -ry * x1p / rx; + + // Step 3: Compute (cx, cy) from (cx', cy') + x_avg = (last_point.x() + next_point.x()) / 2.0f; + y_avg = (last_point.y() + next_point.y()) / 2.0f; + double cx = x_axis_rotation_c * cxp - x_axis_rotation_s * cyp + x_avg; + double cy = x_axis_rotation_s * cxp + x_axis_rotation_c * cyp + y_avg; + + double theta_1 = atan2((y1p - cyp) / ry, (x1p - cxp) / rx); + double theta_2 = atan2((-y1p - cyp) / ry, (-x1p - cxp) / rx); + + auto theta_delta = theta_2 - theta_1; + + if (sweep_flag == 0 && theta_delta > 0.0f) { + theta_delta -= M_TAU; + } else if (sweep_flag != 0 && theta_delta < 0) { + theta_delta += M_TAU; + } + + path.elliptical_arc_to(next_point, { cx, cy }, { rx, ry }, x_axis_rotation, theta_1, theta_delta); + + break; + } + case PathInstructionType::QuadraticBezierCurve: { + clear_last_control_point = false; + + Gfx::FloatPoint through = { data[0], data[1] }; + Gfx::FloatPoint point = { data[2], data[3] }; + + if (absolute) { + path.quadratic_bezier_curve_to(through, point); + m_previous_control_point = through; + } else { + ASSERT(!path.segments().is_empty()); + auto last_point = path.segments().last().point(); + auto control_point = through + last_point; + path.quadratic_bezier_curve_to(control_point, point + last_point); + m_previous_control_point = control_point; + } + break; + } + case PathInstructionType::SmoothQuadraticBezierCurve: { + clear_last_control_point = false; + + ASSERT(!path.segments().is_empty()); + auto last_point = path.segments().last().point(); + + if (m_previous_control_point.is_null()) { + m_previous_control_point = last_point; + } + + auto dx_end_control = last_point.dx_relative_to(m_previous_control_point); + auto dy_end_control = last_point.dy_relative_to(m_previous_control_point); + auto control_point = Gfx::FloatPoint { last_point.x() + dx_end_control, last_point.y() + dy_end_control }; + + Gfx::FloatPoint end_point = { data[0], data[1] }; + + if (absolute) { + path.quadratic_bezier_curve_to(control_point, end_point); + } else { + path.quadratic_bezier_curve_to(control_point, end_point + last_point); + } + + m_previous_control_point = control_point; + break; + } + + case PathInstructionType::Curve: + case PathInstructionType::SmoothCurve: + // Instead of crashing the browser every time we come across an SVG + // with these path instructions, let's just skip them + continue; + case PathInstructionType::Invalid: + ASSERT_NOT_REACHED(); + } + + if (clear_last_control_point) { + m_previous_control_point = Gfx::FloatPoint {}; + } + } + + m_path = path; + return m_path.value(); +} + +} diff --git a/Userland/Libraries/LibWeb/SVG/SVGPathElement.h b/Userland/Libraries/LibWeb/SVG/SVGPathElement.h new file mode 100644 index 0000000000..738df73a13 --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGPathElement.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibGfx/Bitmap.h> +#include <LibWeb/HTML/HTMLElement.h> +#include <LibWeb/SVG/SVGGeometryElement.h> + +namespace Web::SVG { + +enum class PathInstructionType { + Move, + ClosePath, + Line, + HorizontalLine, + VerticalLine, + Curve, + SmoothCurve, + QuadraticBezierCurve, + SmoothQuadraticBezierCurve, + EllipticalArc, + Invalid, +}; + +struct PathInstruction { + PathInstructionType type; + bool absolute; + Vector<float> data; +}; + +class PathDataParser final { +public: + PathDataParser(const String& source); + ~PathDataParser() = default; + + Vector<PathInstruction> parse(); + +private: + void parse_drawto(); + + void parse_moveto(); + void parse_closepath(); + void parse_lineto(); + void parse_horizontal_lineto(); + void parse_vertical_lineto(); + void parse_curveto(); + void parse_smooth_curveto(); + void parse_quadratic_bezier_curveto(); + void parse_smooth_quadratic_bezier_curveto(); + void parse_elliptical_arc(); + + float parse_coordinate(); + Vector<float> parse_coordinate_pair(); + Vector<float> parse_coordinate_sequence(); + Vector<Vector<float>> parse_coordinate_pair_sequence(); + Vector<float> parse_coordinate_pair_double(); + Vector<float> parse_coordinate_pair_triplet(); + Vector<float> parse_elliptical_arg_argument(); + void parse_whitespace(bool must_match_once = false); + void parse_comma_whitespace(); + float parse_fractional_constant(); + float parse_number(); + float parse_flag(); + // -1 if negative, +1 otherwise + int parse_sign(); + + bool match_whitespace() const; + bool match_comma_whitespace() const; + bool match_coordinate() const; + bool match(char c) const { return !done() && ch() == c; } + + bool done() const { return m_cursor >= m_source.length(); } + char ch() const { return m_source[m_cursor]; } + char consume() { return m_source[m_cursor++]; } + + String m_source; + size_t m_cursor { 0 }; + Vector<PathInstruction> m_instructions; +}; + +class SVGPathElement final : public SVGGeometryElement { +public: + using WrapperType = Bindings::SVGPathElementWrapper; + + SVGPathElement(DOM::Document&, const QualifiedName& qualified_name); + virtual ~SVGPathElement() override = default; + + virtual RefPtr<Layout::Node> create_layout_node() override; + + virtual void parse_attribute(const FlyString& name, const String& value) override; + + Gfx::Path& get_path(); + +private: + Vector<PathInstruction> m_instructions; + Gfx::FloatPoint m_previous_control_point = {}; + Optional<Gfx::Path> m_path; +}; + +} diff --git a/Userland/Libraries/LibWeb/SVG/SVGPathElement.idl b/Userland/Libraries/LibWeb/SVG/SVGPathElement.idl new file mode 100644 index 0000000000..d0c195bf25 --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGPathElement.idl @@ -0,0 +1,3 @@ +interface SVGPathElement : SVGGeometryElement { + +}; diff --git a/Userland/Libraries/LibWeb/SVG/SVGSVGElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGSVGElement.cpp new file mode 100644 index 0000000000..ad0eb2e637 --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGSVGElement.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibGfx/Painter.h> +#include <LibWeb/CSS/StyleResolver.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/Layout/SVGSVGBox.h> +#include <LibWeb/SVG/SVGPathElement.h> +#include <LibWeb/SVG/SVGSVGElement.h> +#include <ctype.h> + +namespace Web::SVG { + +SVGSVGElement::SVGSVGElement(DOM::Document& document, const QualifiedName& qualified_name) + : SVGGraphicsElement(document, qualified_name) +{ +} + +RefPtr<Layout::Node> SVGSVGElement::create_layout_node() +{ + auto style = document().style_resolver().resolve_style(*this); + if (style->display() == CSS::Display::None) + return nullptr; + return adopt(*new Layout::SVGSVGBox(document(), *this, move(style))); +} + +unsigned SVGSVGElement::width() const +{ + return attribute(HTML::AttributeNames::width).to_uint().value_or(300); +} + +unsigned SVGSVGElement::height() const +{ + return attribute(HTML::AttributeNames::height).to_uint().value_or(150); +} + +} diff --git a/Userland/Libraries/LibWeb/SVG/SVGSVGElement.h b/Userland/Libraries/LibWeb/SVG/SVGSVGElement.h new file mode 100644 index 0000000000..c84a483d07 --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGSVGElement.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibGfx/Bitmap.h> +#include <LibWeb/SVG/SVGGraphicsElement.h> + +namespace Web::SVG { + +class SVGSVGElement final : public SVGGraphicsElement { +public: + using WrapperType = Bindings::SVGSVGElementWrapper; + + SVGSVGElement(DOM::Document&, const QualifiedName& qualified_name); + + virtual RefPtr<Layout::Node> create_layout_node() override; + + unsigned width() const; + unsigned height() const; + +private: + RefPtr<Gfx::Bitmap> m_bitmap; +}; + +} diff --git a/Userland/Libraries/LibWeb/SVG/SVGSVGElement.idl b/Userland/Libraries/LibWeb/SVG/SVGSVGElement.idl new file mode 100644 index 0000000000..7182cc2389 --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/SVGSVGElement.idl @@ -0,0 +1,3 @@ +interface SVGSVGElement : SVGGraphicsElement { + +}; diff --git a/Userland/Libraries/LibWeb/SVG/TagNames.cpp b/Userland/Libraries/LibWeb/SVG/TagNames.cpp new file mode 100644 index 0000000000..654e914181 --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/TagNames.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <LibWeb/SVG/TagNames.h> + +namespace Web::SVG::TagNames { + +#define __ENUMERATE_SVG_TAG(name) FlyString name; +ENUMERATE_SVG_TAGS +#undef __ENUMERATE_SVG_TAG + +[[gnu::constructor]] static void initialize() +{ + static bool s_initialized = false; + if (s_initialized) + return; + +#define __ENUMERATE_SVG_TAG(name) name = #name; + ENUMERATE_SVG_TAGS +#undef __ENUMERATE_SVG_TAG + + s_initialized = true; +} + +} diff --git a/Userland/Libraries/LibWeb/SVG/TagNames.h b/Userland/Libraries/LibWeb/SVG/TagNames.h new file mode 100644 index 0000000000..d24d5f95de --- /dev/null +++ b/Userland/Libraries/LibWeb/SVG/TagNames.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> + * 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 <AK/FlyString.h> + +namespace Web::SVG::TagNames { + +#define ENUMERATE_SVG_GRAPHICS_TAGS \ + __ENUMERATE_SVG_TAG(path) \ + __ENUMERATE_SVG_TAG(svg) + +#define ENUMERATE_SVG_TAGS \ + ENUMERATE_SVG_GRAPHICS_TAGS \ + __ENUMERATE_SVG_TAG(desc) \ + __ENUMERATE_SVG_TAG(foreignObject) \ + __ENUMERATE_SVG_TAG(script) \ + __ENUMERATE_SVG_TAG(title) + +#define __ENUMERATE_SVG_TAG(name) extern FlyString name; +ENUMERATE_SVG_TAGS +#undef __ENUMERATE_SVG_TAG + +} diff --git a/Userland/Libraries/LibWeb/Scripts/GenerateStyleSheetSource.sh b/Userland/Libraries/LibWeb/Scripts/GenerateStyleSheetSource.sh new file mode 100755 index 0000000000..aef61fcfae --- /dev/null +++ b/Userland/Libraries/LibWeb/Scripts/GenerateStyleSheetSource.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +echo "namespace Web::CSS {" +echo "extern const char $1[];" +echo "const char $1[] = \"\\" +grep -v '^ *#' < "$2" | while IFS= read -r line; do + echo "$line""\\" +done +echo "\";" +echo "}" diff --git a/Userland/Libraries/LibWeb/StylePropertiesModel.cpp b/Userland/Libraries/LibWeb/StylePropertiesModel.cpp new file mode 100644 index 0000000000..123337db8e --- /dev/null +++ b/Userland/Libraries/LibWeb/StylePropertiesModel.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018-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/QuickSort.h> +#include <LibWeb/CSS/PropertyID.h> +#include <LibWeb/CSS/StyleProperties.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/StylePropertiesModel.h> + +namespace Web { + +StylePropertiesModel::StylePropertiesModel(const CSS::StyleProperties& properties) + : m_properties(properties) +{ + properties.for_each_property([&](auto property_id, auto& property_value) { + Value value; + value.name = CSS::string_from_property_id(property_id); + value.value = property_value.to_string(); + m_values.append(value); + }); + + quick_sort(m_values, [](auto& a, auto& b) { return a.name < b.name; }); +} + +int StylePropertiesModel::row_count(const GUI::ModelIndex&) const +{ + return m_values.size(); +} + +String StylePropertiesModel::column_name(int column_index) const +{ + switch (column_index) { + case Column::PropertyName: + return "Name"; + case Column::PropertyValue: + return "Value"; + default: + ASSERT_NOT_REACHED(); + } +} +GUI::Variant StylePropertiesModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const +{ + auto& value = m_values[index.row()]; + if (role == GUI::ModelRole::Display) { + if (index.column() == Column::PropertyName) + return value.name; + if (index.column() == Column::PropertyValue) + return value.value; + } + return {}; +} + +void StylePropertiesModel::update() +{ + did_update(); +} + +} diff --git a/Userland/Libraries/LibWeb/StylePropertiesModel.h b/Userland/Libraries/LibWeb/StylePropertiesModel.h new file mode 100644 index 0000000000..2c260b62f1 --- /dev/null +++ b/Userland/Libraries/LibWeb/StylePropertiesModel.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018-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 <AK/NonnullRefPtrVector.h> +#include <LibGUI/Model.h> + +namespace Web { + +class StyleProperties; + +class StylePropertiesModel final : public GUI::Model { +public: + enum Column { + PropertyName, + PropertyValue, + __Count + }; + + static NonnullRefPtr<StylePropertiesModel> create(const CSS::StyleProperties& properties) { return adopt(*new StylePropertiesModel(properties)); } + + virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; + virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return Column::__Count; } + virtual String column_name(int) const override; + virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; + virtual void update() override; + +private: + explicit StylePropertiesModel(const CSS::StyleProperties& properties); + const CSS::StyleProperties& properties() const { return *m_properties; } + + NonnullRefPtr<CSS::StyleProperties> m_properties; + + struct Value { + String name; + String value; + }; + Vector<Value> m_values; +}; + +} diff --git a/Userland/Libraries/LibWeb/Tests/DOM/Comment.js b/Userland/Libraries/LibWeb/Tests/DOM/Comment.js new file mode 100644 index 0000000000..0dab522e9c --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/DOM/Comment.js @@ -0,0 +1,15 @@ +loadPage("file:///home/anon/web-tests/Pages/Comment.html"); + +afterInitialPageLoad(() => { + test("Basic functionality", () => { + const comment = document.body.firstChild.nextSibling; + expect(comment).not.toBeNull(); + + // FIXME: Add this in once Comment's constructor is implemented. + //expect(comment).toBeInstanceOf(Comment); + + expect(comment.nodeName).toBe("#comment"); + expect(comment.data).toBe("This is a comment"); + expect(comment).toHaveLength(17); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/DOM/Element.js b/Userland/Libraries/LibWeb/Tests/DOM/Element.js new file mode 100644 index 0000000000..16606829aa --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/DOM/Element.js @@ -0,0 +1,39 @@ +loadPage("file:///res/html/misc/innertext_textcontent.html"); + +afterInitialPageLoad(() => { + test("Element.innerText", () => { + var p = document.getElementsByTagName("p")[0]; + expect(p.innerText).toBe("This is a very small test page :^)"); + + // FIXME: Call this on p once that's supported. + var b = document.getElementsByTagName("b")[0]; + b.innerText = "foo"; + expect(b.innerText).toBe("foo"); + expect(p.innerText).toBe("This is a foo test page :^)"); + + p.innerText = "bar"; + expect(p.innerText).toBe("bar"); + + var p = document.getElementById("source"); + // FIXME: The leading and trailing two spaces each are wrong. + // FIXME: The text should be affected by the text-transform:uppercase. + expect(p.innerText).toBe(` Take a look at +how this text +is interpreted below. `); + }); + + test("Element.namespaceURI basics", () => { + const htmlNamespace = "http://www.w3.org/1999/xhtml"; + const p = document.getElementsByTagName("p")[0]; + expect(p.namespaceURI).toBe(htmlNamespace); + + // createElement always sets the namespace to the HTML namespace in HTML documents. + const svgElement = document.createElement("svg"); + expect(svgElement.namespaceURI).toBe(htmlNamespace); + + const svgNamespace = "http://www.w3.org/2000/svg"; + p.innerHTML = "<svg></svg>"; + const domSVGElement = p.getElementsByTagName("svg")[0]; + expect(domSVGElement.namespaceURI).toBe(svgNamespace); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/DOM/Node.js b/Userland/Libraries/LibWeb/Tests/DOM/Node.js new file mode 100644 index 0000000000..5427d143bd --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/DOM/Node.js @@ -0,0 +1,28 @@ +loadPage("file:///res/html/misc/innertext_textcontent.html"); + +afterInitialPageLoad(() => { + test("Node.textContent", () => { + var p = document.getElementsByTagName("p")[0]; + expect(p.textContent).toBe("This is a very small test page :^)"); + expect(p.firstChild.textContent).toBe("This is a "); + expect(p.firstChild.firstChild).toBe(null); + + p.firstChild.textContent = "foo"; + expect(p.firstChild.textContent).toBe("foo"); + expect(p.firstChild.firstChild).toBe(null); + expect(p.textContent).toBe("foovery small test page :^)"); + + p.textContent = "bar"; + expect(p.textContent).toBe("bar"); + expect(p.firstChild.textContent).toBe("bar"); + expect(p.firstChild.firstChild).toBe(null); + + var p = document.getElementById("source"); + expect(p.textContent).toBe(` + #source { color: red; } #text { text-transform: uppercase; } + Take a look athow this textis interpreted + below. + HIDDEN TEXT + `); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/DOM/Text.js b/Userland/Libraries/LibWeb/Tests/DOM/Text.js new file mode 100644 index 0000000000..1557a59d3f --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/DOM/Text.js @@ -0,0 +1,15 @@ +loadPage("file:///res/html/misc/blank.html"); + +afterInitialPageLoad(() => { + test("Basic functionality", () => { + const title = document.getElementsByTagName("title")[0]; + expect(title).toBeDefined(); + + // FIXME: Add this in once Text's constructor is implemented. + //expect(title.firstChild).toBeInstanceOf(Text); + + expect(title.firstChild.nodeName).toBe("#text"); + expect(title.firstChild.data).toBe("Blank"); + expect(title.firstChild.length).toBe(5); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/DOM/document.createComment.js b/Userland/Libraries/LibWeb/Tests/DOM/document.createComment.js new file mode 100644 index 0000000000..a60d6bfcd5 --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/DOM/document.createComment.js @@ -0,0 +1,13 @@ +loadPage("file:///res/html/misc/blank.html"); + +afterInitialPageLoad(() => { + test("Basic functionality", () => { + const comment = document.createComment("Create Comment Test"); + + // FIXME: Add this in once Comment's constructor is implemented. + //expect(comment).toBeInstanceOf(Comment); + expect(comment.nodeName).toBe("#comment"); + expect(comment.data).toBe("Create Comment Test"); + expect(comment.length).toBe(19); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/DOM/document.createDocumentFragment.js b/Userland/Libraries/LibWeb/Tests/DOM/document.createDocumentFragment.js new file mode 100644 index 0000000000..bebc137313 --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/DOM/document.createDocumentFragment.js @@ -0,0 +1,11 @@ +loadPage("file:///res/html/misc/blank.html"); + +afterInitialPageLoad(() => { + test("Basic functionality", () => { + const fragment = document.createDocumentFragment(); + + // FIXME: Add this in once DocumentFragment's constructor is implemented. + //expect(fragment).toBeInstanceOf(DocumentFragment); + expect(fragment.nodeName).toBe("#document-fragment"); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/DOM/document.createTextNode.js b/Userland/Libraries/LibWeb/Tests/DOM/document.createTextNode.js new file mode 100644 index 0000000000..bef06a81c7 --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/DOM/document.createTextNode.js @@ -0,0 +1,13 @@ +loadPage("file:///res/html/misc/blank.html"); + +afterInitialPageLoad(() => { + test("Basic functionality", () => { + const text = document.createTextNode("Create Text Test"); + + // FIXME: Add this in once Text's constructor is implemented. + //expect(text).toBeInstanceOf(Text); + expect(text.nodeName).toBe("#text"); + expect(text.data).toBe("Create Text Test"); + expect(text.length).toBe(16); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/DOM/document.doctype.js b/Userland/Libraries/LibWeb/Tests/DOM/document.doctype.js new file mode 100644 index 0000000000..5da52014d1 --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/DOM/document.doctype.js @@ -0,0 +1,18 @@ +loadPage("file:///res/html/misc/blank.html"); + +afterInitialPageLoad(() => { + test("Basic functionality", () => { + expect(document.compatMode).toBe("CSS1Compat"); + expect(document.doctype).not.toBeNull(); + expect(document.doctype.name).toBe("html"); + expect(document.doctype.publicId).toBe(""); + expect(document.doctype.systemId).toBe(""); + }); + + libweb_tester.changePage("file:///res/html/misc/blank-no-doctype.html"); + + test("Quirks mode", () => { + expect(document.compatMode).toBe("BackCompat"); + expect(document.doctype).toBeNull(); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/DOM/document.documentElement.js b/Userland/Libraries/LibWeb/Tests/DOM/document.documentElement.js new file mode 100644 index 0000000000..86d885c709 --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/DOM/document.documentElement.js @@ -0,0 +1,16 @@ +loadPage("file:///res/html/misc/blank.html"); + +afterInitialPageLoad(() => { + test("Basic functionality", () => { + expect(document.documentElement).not.toBeNull(); + // FIXME: Add this in once HTMLHtmlElement's constructor is implemented. + //expect(document.documentElement).toBeInstanceOf(HTMLHtmlElement); + expect(document.documentElement.nodeName).toBe("html"); + }); + + // FIXME: Add this in once removeChild is implemented. + test.skip("Nullable", () => { + document.removeChild(document.documentElement); + expect(document.documentElement).toBeNull(); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/DOM/mixins/NonElementParentNode.js b/Userland/Libraries/LibWeb/Tests/DOM/mixins/NonElementParentNode.js new file mode 100644 index 0000000000..5b269025f4 --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/DOM/mixins/NonElementParentNode.js @@ -0,0 +1,19 @@ +loadPage("file:///home/anon/web-tests/Pages/ParentNode.html"); + +afterInitialPageLoad(() => { + test("getElementById basics", () => { + const unique = document.getElementById("unique"); + expect(unique).not.toBeNull(); + expect(unique.nodeName).toBe("div"); + expect(unique.id).toBe("unique"); + + const caseSensitive = document.getElementById("Unique"); + expect(caseSensitive).toBeNull(); + + const firstDuplicate = document.getElementById("dupeId"); + expect(firstDuplicate).not.toBeNull(); + expect(firstDuplicate.nodeName).toBe("div"); + expect(firstDuplicate.id).toBe("dupeId"); + expect(firstDuplicate.innerHTML).toBe("First ID"); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/DOM/mixins/ParentNode.js b/Userland/Libraries/LibWeb/Tests/DOM/mixins/ParentNode.js new file mode 100644 index 0000000000..3558c0352f --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/DOM/mixins/ParentNode.js @@ -0,0 +1,25 @@ +loadPage("file:///home/anon/web-tests/Pages/ParentNode.html"); + +afterInitialPageLoad(() => { + test("querySelector basics", () => { + const firstDuplicateElement = document.querySelector(".duplicate"); + expect(firstDuplicateElement).not.toBeNull(); + expect(firstDuplicateElement.nodeName).toBe("div"); + expect(firstDuplicateElement.innerHTML).toBe("First"); + + const noElement = document.querySelector(".nonexistent"); + expect(noElement).toBeNull(); + }); + + test("querySelectorAll basics", () => { + const allDuplicates = document.querySelectorAll(".duplicate"); + expect(allDuplicates).toHaveLength(2); + expect(allDuplicates[0].nodeName).toBe("div"); + expect(allDuplicates[0].innerHTML).toBe("First"); + expect(allDuplicates[1].nodeName).toBe("div"); + expect(allDuplicates[1].innerHTML).toBe("Second"); + + const noElements = document.querySelectorAll(".nonexistent"); + expect(noElements).toHaveLength(0); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/HTML/HTMLElement.js b/Userland/Libraries/LibWeb/Tests/HTML/HTMLElement.js new file mode 100644 index 0000000000..2950bb45c5 --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/HTML/HTMLElement.js @@ -0,0 +1,9 @@ +loadPage("file:///res/html/misc/welcome.html"); + +afterInitialPageLoad(() => { + test("contentEditable attribute", () => { + expect(document.body.contentEditable).toBe("inherit"); + expect(document.firstChild.nextSibling.nodeName).toBe("html"); + expect(document.firstChild.nextSibling.contentEditable).toBe("true"); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/HTML/HTMLTemplateElement.js b/Userland/Libraries/LibWeb/Tests/HTML/HTMLTemplateElement.js new file mode 100644 index 0000000000..dd6c66e1f2 --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/HTML/HTMLTemplateElement.js @@ -0,0 +1,27 @@ +loadPage("file:///home/anon/web-tests/Pages/Template.html"); + +afterInitialPageLoad(() => { + test("Basic functionality", () => { + const template = document.getElementById("template"); + expect(template).not.toBeNull(); + + // The contents of a template element are not children of the actual element. + // The document fragment is not a child of the element either. + expect(template.firstChild).toBeNull(); + + // FIXME: Add this in once DocumentFragment's constructor is implemented. + //expect(template.content).toBeInstanceOf(DocumentFragment); + expect(template.content.nodeName).toBe("#document-fragment"); + + const templateDiv = template.content.getElementById("templatediv"); + expect(templateDiv.nodeName).toBe("div"); + expect(templateDiv.textContent).toBe("Hello template!"); + }); + + test("Templates are inert (e.g. scripts won't run)", () => { + // The page has a template element with a script element in it. + // Templates are inert, for example, they won't run scripts. + // That script will set "templateScriptRan" to true if it ran. + expect(window.templateScriptRan).toBeUndefined(); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/HTML/document.body.js b/Userland/Libraries/LibWeb/Tests/HTML/document.body.js new file mode 100644 index 0000000000..9fc1907583 --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/HTML/document.body.js @@ -0,0 +1,55 @@ +loadPage("file:///res/html/misc/blank.html"); + +afterInitialPageLoad(() => { + test("Basic functionality", () => { + expect(document.body).not.toBeNull(); + // FIXME: Add this in once HTMLBodyElement's constructor is implemented. + //expect(document.body).toBeInstanceOf(HTMLBodyElement); + expect(document.body.nodeName).toBe("body"); + }); + + // FIXME: Add this in once set_body is fully implemented. + test.skip("Setting body to a new body element", () => { + // Add something to body to see if it's gone afterwards + const p = document.createElement("p"); + document.body.appendChild(p); + + expect(document.body.firstChild).toBe(p); + + const newBody = document.createElement("body"); + document.body = newBody; + + expect(document.body).not.toBeNull(); + expect(document.body.nodeName).toBe("body"); + + // FIXME: Add this in once HTMLBodyElement's constructor is implemented. + //expect(document.body).toBeInstanceOf(HTMLBodyElement); + + expect(document.body.firstChild).toBeNull(); + }); + + // FIXME: Add this in once set_body is fully implemented. + test.skip("Setting body to a new frameset element", () => { + const newFrameSet = document.createElement("frameset"); + document.body = newFrameSet; + + expect(document.body).not.toBeNull(); + expect(document.body.nodeName).toBe("frameset"); + + // FIXME: Add this in once HTMLFrameSetElement's constructor is implemented. + //expect(document.body).toBeInstanceOf(HTMLFrameSetElement); + }); + + // FIXME: Add this in once set_body is fully implemented. + test.skip("Setting body to an element that isn't body/frameset", () => { + expect(() => { + document.body = document.createElement("div"); + }).toThrow(DOMException); + }); + + // FIXME: Add this in once removeChild is implemented. + test.skip("Nullable", () => { + document.documentElement.removeChild(document.body); + expect(document.body).toBeNull(); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/HTML/document.head.js b/Userland/Libraries/LibWeb/Tests/HTML/document.head.js new file mode 100644 index 0000000000..5d997fe3dd --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/HTML/document.head.js @@ -0,0 +1,16 @@ +loadPage("file:///res/html/misc/blank.html"); + +afterInitialPageLoad(() => { + test("Basic functionality", () => { + expect(document.head).not.toBeNull(); + // FIXME: Add this in once HTMLHeadElement's constructor is implemented. + //expect(document.head).toBeInstanceOf(HTMLHeadElement); + expect(document.head.nodeName).toBe("head"); + }); + + // FIXME: Add this in once removeChild is implemented. + test.skip("Nullable", () => { + document.documentElement.removeChild(document.head); + expect(document.head).toBeNull(); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/HTML/document.readyState.js b/Userland/Libraries/LibWeb/Tests/HTML/document.readyState.js new file mode 100644 index 0000000000..12b6c1d8df --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/HTML/document.readyState.js @@ -0,0 +1,31 @@ +loadPage("file:///res/html/misc/blank.html"); + +beforeInitialPageLoad(() => { + window.events = []; + + document.addEventListener("readystatechange", () => { + window.events.push(document.readyState); + }); + + document.addEventListener("DOMContentLoaded", () => { + test("Ready state should be 'interactive' when 'DOMContentLoaded' fires", () => { + expect(document.readyState).toBe("interactive"); + }); + }); + + test("Ready state should be 'loading' initially", () => { + expect(document.readyState).toBe("loading"); + }); +}); + +afterInitialPageLoad(() => { + test("'interactive' should come before 'complete' and both should have happened", () => { + expect(window.events).toHaveLength(2); + expect(window.events[0]).toBe("interactive"); + expect(window.events[1]).toBe("complete"); + }); + + test("Ready state should be 'complete' after loading", () => { + expect(document.readyState).toBe("complete"); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/Pages/Comment.html b/Userland/Libraries/LibWeb/Tests/Pages/Comment.html new file mode 100644 index 0000000000..84ae91695f --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/Pages/Comment.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<html> +<head> +</head> +<body> +<!--This is a comment--> +</body> +</html> diff --git a/Userland/Libraries/LibWeb/Tests/Pages/ParentNode.html b/Userland/Libraries/LibWeb/Tests/Pages/ParentNode.html new file mode 100644 index 0000000000..536b5ab35d --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/Pages/ParentNode.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html> +<head> +</head> +<body> + <div class="duplicate">First</div> + <div class="duplicate">Second</div> + <div id="unique"></div> + <div id="dupeId">First ID</div> + <div id="dupeId">Second ID</div> +</body> +</html> diff --git a/Userland/Libraries/LibWeb/Tests/Pages/Template.html b/Userland/Libraries/LibWeb/Tests/Pages/Template.html new file mode 100644 index 0000000000..e4478f408f --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/Pages/Template.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> +<head> +</head> +<body> +<template id="template"> + <div id="templatediv">Hello template!</div> + <script> + // I shouldn't be run. + window.templateScriptRan = true; + </script> +</template> +</body> +</html> diff --git a/Userland/Libraries/LibWeb/Tests/Window/Base64.js b/Userland/Libraries/LibWeb/Tests/Window/Base64.js new file mode 100644 index 0000000000..c272d70b74 --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/Window/Base64.js @@ -0,0 +1,19 @@ +loadPage("file:///res/html/misc/blank.html"); + +afterInitialPageLoad(() => { + test("atob", () => { + expect(atob("YQ==")).toBe("a"); + expect(atob("YWE=")).toBe("aa"); + expect(atob("YWFh")).toBe("aaa"); + expect(atob("YWFhYQ==")).toBe("aaaa"); + expect(atob("/w==")).toBe("\xff"); + }); + + test("btoa", () => { + expect(btoa("a")).toBe("YQ=="); + expect(btoa("aa")).toBe("YWE="); + expect(btoa("aaa")).toBe("YWFh"); + expect(btoa("aaaa")).toBe("YWFhYQ=="); + expect(btoa("\xff")).toBe("/w=="); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/Window/window.window_frames_self.js b/Userland/Libraries/LibWeb/Tests/Window/window.window_frames_self.js new file mode 100644 index 0000000000..1d7799843e --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/Window/window.window_frames_self.js @@ -0,0 +1,8 @@ +loadPage("file:///res/html/misc/blank.html"); + +afterInitialPageLoad(() => { + test("window.{window,frames,self} all return the Window object", () => { + expect(window.window).toBe(window.frames); + expect(window.window).toBe(window.self); + }); +}); diff --git a/Userland/Libraries/LibWeb/Tests/libweb_tester.d.ts b/Userland/Libraries/LibWeb/Tests/libweb_tester.d.ts new file mode 100644 index 0000000000..23570e70f8 --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/libweb_tester.d.ts @@ -0,0 +1,16 @@ +// NOTE: This file is only for syntax highlighting, documentation, etc. + +interface LibwebTester { + /** + * Changes the page to the specified URL. Everything afterwards will refer to the new page. + * @param url Page to load. + */ + changePage(url: string): void; +} + +interface Window { + /** + * Special test object used to ease test development for LibWeb. + */ + readonly libweb_tester: LibwebTester; +} diff --git a/Userland/Libraries/LibWeb/Tests/test-common.js b/Userland/Libraries/LibWeb/Tests/test-common.js new file mode 100644 index 0000000000..7fc4f914bf --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/test-common.js @@ -0,0 +1,31 @@ +// NOTE: The tester loads in LibJS's test-common to prevent duplication. + +// NOTE: "window.libweb_tester" is set to a special tester object. +// See libweb_tester.d.ts for definitions. + +let __PageToLoad__; + +// This tells the tester which page to load. +// This will only be checked when we look at which page the test wants to use. +// Subsequent calls to loadPage in before/after initial load will be ignored. +let loadPage; + +let __BeforeInitialPageLoad__ = () => {}; + +// This function will be called just before loading the initial page. +// This is useful for injecting event listeners. +// Defaults to an empty function. +let beforeInitialPageLoad; + +let __AfterInitialPageLoad__ = () => {}; + +// This function will be called just after loading the initial page. +// This is where the main bulk of the tests should be. +// Defaults to an empty function. +let afterInitialPageLoad; + +(() => { + loadPage = page => (__PageToLoad__ = page); + beforeInitialPageLoad = callback => (__BeforeInitialPageLoad__ = callback); + afterInitialPageLoad = callback => (__AfterInitialPageLoad__ = callback); +})(); diff --git a/Userland/Libraries/LibWeb/TreeNode.h b/Userland/Libraries/LibWeb/TreeNode.h new file mode 100644 index 0000000000..1673a52dc5 --- /dev/null +++ b/Userland/Libraries/LibWeb/TreeNode.h @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2018-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 <AK/Assertions.h> +#include <AK/NonnullRefPtr.h> +#include <AK/TypeCasts.h> +#include <AK/Weakable.h> +#include <LibWeb/Forward.h> + +namespace Web { + +template<typename T> +class TreeNode : public Weakable<T> { +public: + void ref() + { + ASSERT(!m_in_removed_last_ref); + ASSERT(m_ref_count); + ++m_ref_count; + } + + void unref() + { + ASSERT(!m_in_removed_last_ref); + ASSERT(m_ref_count); + if (!--m_ref_count) { + if constexpr (IsBaseOf<DOM::Node, T>::value) { + m_in_removed_last_ref = true; + static_cast<T*>(this)->removed_last_ref(); + } else { + delete static_cast<T*>(this); + } + return; + } + } + int ref_count() const { return m_ref_count; } + + T* parent() { return m_parent; } + const T* parent() const { return m_parent; } + + bool has_children() const { return m_first_child; } + T* next_sibling() { return m_next_sibling; } + T* previous_sibling() { return m_previous_sibling; } + T* first_child() { return m_first_child; } + T* last_child() { return m_last_child; } + const T* next_sibling() const { return m_next_sibling; } + const T* previous_sibling() const { return m_previous_sibling; } + const T* first_child() const { return m_first_child; } + const T* last_child() const { return m_last_child; } + + int child_count() const + { + int count = 0; + for (auto* child = first_child(); child; child = child->next_sibling()) + ++count; + return count; + } + + T* child_at_index(int index) + { + int count = 0; + for (auto* child = first_child(); child; child = child->next_sibling()) { + if (count == index) + return child; + ++count; + } + return nullptr; + } + + const T* child_at_index(int index) const + { + return const_cast<TreeNode*>(this)->child_at_index(index); + } + + bool is_ancestor_of(const TreeNode&) const; + + void prepend_child(NonnullRefPtr<T> node); + void append_child(NonnullRefPtr<T> node, bool notify = true); + void insert_before(NonnullRefPtr<T> node, RefPtr<T> child, bool notify = true); + NonnullRefPtr<T> remove_child(NonnullRefPtr<T> node); + + void remove_all_children(); + + bool is_child_allowed(const T&) const { return true; } + + T* next_in_pre_order() + { + if (first_child()) + return first_child(); + T* node; + if (!(node = next_sibling())) { + node = parent(); + while (node && !node->next_sibling()) + node = node->parent(); + if (node) + node = node->next_sibling(); + } + return node; + } + + const T* next_in_pre_order() const + { + return const_cast<TreeNode*>(this)->next_in_pre_order(); + } + + bool is_before(const T& other) const + { + if (this == &other) + return false; + for (auto* node = this; node; node = node->next_in_pre_order()) { + if (node == &other) + return true; + } + return false; + } + + template<typename Callback> + IterationDecision for_each_in_subtree(Callback callback) const + { + if (callback(static_cast<const T&>(*this)) == IterationDecision::Break) + return IterationDecision::Break; + for (auto* child = first_child(); child; child = child->next_sibling()) { + if (child->for_each_in_subtree(callback) == IterationDecision::Break) + return IterationDecision::Break; + } + return IterationDecision::Continue; + } + + template<typename Callback> + IterationDecision for_each_in_subtree(Callback callback) + { + if (callback(static_cast<T&>(*this)) == IterationDecision::Break) + return IterationDecision::Break; + for (auto* child = first_child(); child; child = child->next_sibling()) { + if (child->for_each_in_subtree(callback) == IterationDecision::Break) + return IterationDecision::Break; + } + return IterationDecision::Continue; + } + + template<typename U, typename Callback> + IterationDecision for_each_in_subtree_of_type(Callback callback) + { + if (is<U>(static_cast<const T&>(*this))) { + if (callback(static_cast<U&>(*this)) == IterationDecision::Break) + return IterationDecision::Break; + } + for (auto* child = first_child(); child; child = child->next_sibling()) { + if (child->template for_each_in_subtree_of_type<U>(callback) == IterationDecision::Break) + return IterationDecision::Break; + } + return IterationDecision::Continue; + } + + template<typename U, typename Callback> + IterationDecision for_each_in_subtree_of_type(Callback callback) const + { + if (is<U>(static_cast<const T&>(*this))) { + if (callback(static_cast<const U&>(*this)) == IterationDecision::Break) + return IterationDecision::Break; + } + for (auto* child = first_child(); child; child = child->next_sibling()) { + if (child->template for_each_in_subtree_of_type<U>(callback) == IterationDecision::Break) + return IterationDecision::Break; + } + return IterationDecision::Continue; + } + + template<typename Callback> + void for_each_child(Callback callback) const + { + return const_cast<TreeNode*>(this)->template for_each_child(move(callback)); + } + + template<typename Callback> + void for_each_child(Callback callback) + { + for (auto* node = first_child(); node; node = node->next_sibling()) + callback(*node); + } + + template<typename U, typename Callback> + void for_each_child_of_type(Callback callback) + { + for (auto* node = first_child(); node; node = node->next_sibling()) { + if (is<U>(node)) + callback(downcast<U>(*node)); + } + } + + template<typename U, typename Callback> + void for_each_child_of_type(Callback callback) const + { + return const_cast<TreeNode*>(this)->template for_each_child_of_type<U>(move(callback)); + } + + template<typename U> + const U* next_sibling_of_type() const + { + return const_cast<TreeNode*>(this)->template next_sibling_of_type<U>(); + } + + template<typename U> + inline U* next_sibling_of_type() + { + for (auto* sibling = next_sibling(); sibling; sibling = sibling->next_sibling()) { + if (is<U>(*sibling)) + return &downcast<U>(*sibling); + } + return nullptr; + } + + template<typename U> + const U* previous_sibling_of_type() const + { + return const_cast<TreeNode*>(this)->template previous_sibling_of_type<U>(); + } + + template<typename U> + U* previous_sibling_of_type() + { + for (auto* sibling = previous_sibling(); sibling; sibling = sibling->previous_sibling()) { + if (is<U>(*sibling)) + return &downcast<U>(*sibling); + } + return nullptr; + } + + template<typename U> + const U* first_child_of_type() const + { + return const_cast<TreeNode*>(this)->template first_child_of_type<U>(); + } + + template<typename U> + const U* last_child_of_type() const + { + return const_cast<TreeNode*>(this)->template last_child_of_type<U>(); + } + + template<typename U> + U* first_child_of_type() + { + for (auto* child = first_child(); child; child = child->next_sibling()) { + if (is<U>(*child)) + return &downcast<U>(*child); + } + return nullptr; + } + + template<typename U> + U* last_child_of_type() + { + for (auto* child = last_child(); child; child = child->previous_sibling()) { + if (is<U>(*child)) + return &downcast<U>(*child); + } + return nullptr; + } + + template<typename U> + const U* first_ancestor_of_type() const + { + return const_cast<TreeNode*>(this)->template first_ancestor_of_type<U>(); + } + + template<typename U> + U* first_ancestor_of_type() + { + for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) { + if (is<U>(*ancestor)) + return &downcast<U>(*ancestor); + } + return nullptr; + } + +protected: + TreeNode() { } + + bool m_deletion_has_begun { false }; + bool m_in_removed_last_ref { false }; + +private: + int m_ref_count { 1 }; + T* m_parent { nullptr }; + T* m_first_child { nullptr }; + T* m_last_child { nullptr }; + T* m_next_sibling { nullptr }; + T* m_previous_sibling { nullptr }; +}; + +template<typename T> +inline void TreeNode<T>::remove_all_children() +{ + while (RefPtr<T> child = first_child()) + remove_child(child.release_nonnull()); +} + +template<typename T> +inline NonnullRefPtr<T> TreeNode<T>::remove_child(NonnullRefPtr<T> node) +{ + ASSERT(node->m_parent == this); + + if (m_first_child == node) + m_first_child = node->m_next_sibling; + + if (m_last_child == node) + m_last_child = node->m_previous_sibling; + + if (node->m_next_sibling) + node->m_next_sibling->m_previous_sibling = node->m_previous_sibling; + + if (node->m_previous_sibling) + node->m_previous_sibling->m_next_sibling = node->m_next_sibling; + + node->m_next_sibling = nullptr; + node->m_previous_sibling = nullptr; + node->m_parent = nullptr; + + node->removed_from(static_cast<T&>(*this)); + + node->unref(); + + static_cast<T*>(this)->children_changed(); + + return node; +} + +template<typename T> +inline void TreeNode<T>::append_child(NonnullRefPtr<T> node, bool notify) +{ + ASSERT(!node->m_parent); + + if (!static_cast<T*>(this)->is_child_allowed(*node)) + return; + + if (m_last_child) + m_last_child->m_next_sibling = node.ptr(); + node->m_previous_sibling = m_last_child; + node->m_parent = static_cast<T*>(this); + m_last_child = node.ptr(); + if (!m_first_child) + m_first_child = m_last_child; + if (notify) + node->inserted_into(static_cast<T&>(*this)); + [[maybe_unused]] auto& rc = node.leak_ref(); + + if (notify) + static_cast<T*>(this)->children_changed(); +} + +template<typename T> +inline void TreeNode<T>::insert_before(NonnullRefPtr<T> node, RefPtr<T> child, bool notify) +{ + if (!child) + return append_child(move(node), notify); + + ASSERT(!node->m_parent); + ASSERT(child->parent() == this); + + if (!static_cast<T*>(this)->is_child_allowed(*node)) + return; + + node->m_previous_sibling = child->m_previous_sibling; + node->m_next_sibling = child; + + if (child->m_previous_sibling) + child->m_previous_sibling->m_next_sibling = node; + + if (m_first_child == child) + m_first_child = node; + + child->m_previous_sibling = node; + + if (m_first_child == child) + m_first_child = node; + + node->m_parent = static_cast<T*>(this); + if (notify) + node->inserted_into(static_cast<T&>(*this)); + [[maybe_unused]] auto& rc = node.leak_ref(); + + if (notify) + static_cast<T*>(this)->children_changed(); +} + +template<typename T> +inline void TreeNode<T>::prepend_child(NonnullRefPtr<T> node) +{ + ASSERT(!node->m_parent); + + if (!static_cast<T*>(this)->is_child_allowed(*node)) + return; + + if (m_first_child) + m_first_child->m_previous_sibling = node.ptr(); + node->m_next_sibling = m_first_child; + node->m_parent = static_cast<T*>(this); + m_first_child = node.ptr(); + if (!m_last_child) + m_last_child = m_first_child; + node->inserted_into(static_cast<T&>(*this)); + [[maybe_unused]] auto& rc = node.leak_ref(); + + static_cast<T*>(this)->children_changed(); +} + +template<typename T> +inline bool TreeNode<T>::is_ancestor_of(const TreeNode<T>& other) const +{ + for (auto* ancestor = other.parent(); ancestor; ancestor = ancestor->parent()) { + if (ancestor == this) + return true; + } + return false; +} + +} diff --git a/Userland/Libraries/LibWeb/UIEvents/EventNames.cpp b/Userland/Libraries/LibWeb/UIEvents/EventNames.cpp new file mode 100644 index 0000000000..4832ab493b --- /dev/null +++ b/Userland/Libraries/LibWeb/UIEvents/EventNames.cpp @@ -0,0 +1,49 @@ +/* + * 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 <LibWeb/UIEvents/EventNames.h> + +namespace Web::UIEvents::EventNames { + +#define __ENUMERATE_UI_EVENT(name) FlyString name; +ENUMERATE_UI_EVENTS +#undef __ENUMERATE_UI_EVENT + +[[gnu::constructor]] static void initialize() +{ + static bool s_initialized = false; + if (s_initialized) + return; + +#define __ENUMERATE_UI_EVENT(name) \ + name = #name; + ENUMERATE_UI_EVENTS +#undef __ENUMERATE_UI_EVENT + + s_initialized = true; +} + +} diff --git a/Userland/Libraries/LibWeb/UIEvents/EventNames.h b/Userland/Libraries/LibWeb/UIEvents/EventNames.h new file mode 100644 index 0000000000..3667a32f9b --- /dev/null +++ b/Userland/Libraries/LibWeb/UIEvents/EventNames.h @@ -0,0 +1,49 @@ +/* + * 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 <AK/FlyString.h> + +namespace Web::UIEvents::EventNames { + +// FIXME: This is not all of the events + +#define ENUMERATE_UI_EVENTS \ + __ENUMERATE_UI_EVENT(click) \ + __ENUMERATE_UI_EVENT(mousedown) \ + __ENUMERATE_UI_EVENT(mouseenter) \ + __ENUMERATE_UI_EVENT(mouseleave) \ + __ENUMERATE_UI_EVENT(mousemove) \ + __ENUMERATE_UI_EVENT(mouseout) \ + __ENUMERATE_UI_EVENT(mouseover) \ + __ENUMERATE_UI_EVENT(mouseup) + +#define __ENUMERATE_UI_EVENT(name) extern FlyString name; +ENUMERATE_UI_EVENTS +#undef __ENUMERATE_UI_EVENT + +} diff --git a/Userland/Libraries/LibWeb/UIEvents/MouseEvent.cpp b/Userland/Libraries/LibWeb/UIEvents/MouseEvent.cpp new file mode 100644 index 0000000000..e102a4e138 --- /dev/null +++ b/Userland/Libraries/LibWeb/UIEvents/MouseEvent.cpp @@ -0,0 +1,54 @@ +/* + * 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 <LibWeb/HTML/EventNames.h> +#include <LibWeb/UIEvents/EventNames.h> +#include <LibWeb/UIEvents/MouseEvent.h> + +namespace Web::UIEvents { + +MouseEvent::MouseEvent(const FlyString& event_name, i32 offset_x, i32 offset_y) + : UIEvent(event_name) + , m_offset_x(offset_x) + , m_offset_y(offset_y) +{ + set_event_characteristics(); +} + +MouseEvent::~MouseEvent() +{ +} + +void MouseEvent::set_event_characteristics() +{ + if (type().is_one_of(EventNames::mousedown, EventNames::mousemove, EventNames::mouseout, EventNames::mouseover, EventNames::mouseup, HTML::EventNames::click)) { + set_bubbles(true); + set_cancelable(true); + set_composed(true); + } +} + +} diff --git a/Userland/Libraries/LibWeb/UIEvents/MouseEvent.h b/Userland/Libraries/LibWeb/UIEvents/MouseEvent.h new file mode 100644 index 0000000000..13fc8d3322 --- /dev/null +++ b/Userland/Libraries/LibWeb/UIEvents/MouseEvent.h @@ -0,0 +1,58 @@ +/* + * 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 <AK/TypeCasts.h> +#include <LibWeb/UIEvents/UIEvent.h> + +namespace Web::UIEvents { + +class MouseEvent final : public UIEvents::UIEvent { +public: + using WrapperType = Bindings::MouseEventWrapper; + + static NonnullRefPtr<MouseEvent> create(const FlyString& event_name, i32 offset_x, i32 offset_y) + { + return adopt(*new MouseEvent(event_name, offset_x, offset_y)); + } + + virtual ~MouseEvent() override; + + i32 offset_x() const { return m_offset_x; } + i32 offset_y() const { return m_offset_y; } + +protected: + MouseEvent(const FlyString& event_name, i32 offset_x, i32 offset_y); + +private: + void set_event_characteristics(); + + i32 m_offset_x { 0 }; + i32 m_offset_y { 0 }; +}; + +} diff --git a/Userland/Libraries/LibWeb/UIEvents/MouseEvent.idl b/Userland/Libraries/LibWeb/UIEvents/MouseEvent.idl new file mode 100644 index 0000000000..2751f2353a --- /dev/null +++ b/Userland/Libraries/LibWeb/UIEvents/MouseEvent.idl @@ -0,0 +1,6 @@ +interface MouseEvent : Event { + + readonly attribute double offsetX; + readonly attribute double offsetY; + +}; diff --git a/Userland/Libraries/LibWeb/UIEvents/UIEvent.h b/Userland/Libraries/LibWeb/UIEvents/UIEvent.h new file mode 100644 index 0000000000..cbb8ee7de9 --- /dev/null +++ b/Userland/Libraries/LibWeb/UIEvents/UIEvent.h @@ -0,0 +1,46 @@ +/* + * 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/DOM/Event.h> + +namespace Web::UIEvents { + +class UIEvent : public DOM::Event { +public: + using WrapperType = Bindings::UIEventWrapper; + + virtual ~UIEvent() override { } + +protected: + explicit UIEvent(const FlyString& event_name) + : Event(event_name) + { + } +}; + +} diff --git a/Userland/Libraries/LibWeb/UIEvents/UIEvent.idl b/Userland/Libraries/LibWeb/UIEvents/UIEvent.idl new file mode 100644 index 0000000000..f885f4669a --- /dev/null +++ b/Userland/Libraries/LibWeb/UIEvents/UIEvent.idl @@ -0,0 +1,3 @@ +interface UIEvent : Event { + +}; diff --git a/Userland/Libraries/LibWeb/URLEncoder.cpp b/Userland/Libraries/LibWeb/URLEncoder.cpp new file mode 100644 index 0000000000..a6be0174ea --- /dev/null +++ b/Userland/Libraries/LibWeb/URLEncoder.cpp @@ -0,0 +1,46 @@ +/* + * 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 <AK/StringBuilder.h> +#include <AK/URLParser.h> +#include <LibWeb/URLEncoder.h> + +namespace Web { + +String urlencode(const Vector<URLQueryParam>& pairs) +{ + StringBuilder builder; + for (size_t i = 0; i < pairs.size(); ++i) { + builder.append(urlencode(pairs[i].name)); + builder.append('='); + builder.append(urlencode(pairs[i].value)); + if (i != pairs.size() - 1) + builder.append('&'); + } + return builder.to_string(); +} + +} diff --git a/Userland/Libraries/LibWeb/URLEncoder.h b/Userland/Libraries/LibWeb/URLEncoder.h new file mode 100644 index 0000000000..ad8d3d14aa --- /dev/null +++ b/Userland/Libraries/LibWeb/URLEncoder.h @@ -0,0 +1,41 @@ +/* + * 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 <AK/String.h> +#include <AK/Vector.h> + +namespace Web { + +struct URLQueryParam { + String name; + String value; +}; + +String urlencode(const Vector<URLQueryParam>&); + +} diff --git a/Userland/Libraries/LibWeb/WebContentClient.cpp b/Userland/Libraries/LibWeb/WebContentClient.cpp new file mode 100644 index 0000000000..870b691d3b --- /dev/null +++ b/Userland/Libraries/LibWeb/WebContentClient.cpp @@ -0,0 +1,149 @@ +/* + * 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 "WebContentClient.h" +#include "OutOfProcessWebView.h" +#include <AK/SharedBuffer.h> + +namespace Web { + +WebContentClient::WebContentClient(OutOfProcessWebView& view) + : IPC::ServerConnection<WebContentClientEndpoint, WebContentServerEndpoint>(*this, "/tmp/portal/webcontent") + , m_view(view) +{ + handshake(); +} + +void WebContentClient::handshake() +{ + auto response = send_sync<Messages::WebContentServer::Greet>(getpid()); + set_my_client_id(response->client_id()); + set_server_pid(response->server_pid()); +} + +void WebContentClient::handle(const Messages::WebContentClient::DidPaint& message) +{ +#ifdef DEBUG_SPAM + dbg() << "handle: WebContentClient::DidPaint! content_rect=" << message.content_rect() << ", shbuf_id=" << message.shbuf_id(); +#endif + m_view.notify_server_did_paint({}, message.shbuf_id()); +} + +void WebContentClient::handle([[maybe_unused]] const Messages::WebContentClient::DidFinishLoading& message) +{ + m_view.notify_server_did_finish_loading({}, message.url()); +} + +void WebContentClient::handle(const Messages::WebContentClient::DidInvalidateContentRect& message) +{ +#ifdef DEBUG_SPAM + dbg() << "handle: WebContentClient::DidInvalidateContentRect! content_rect=" << message.content_rect(); +#endif + + // FIXME: Figure out a way to coalesce these messages to reduce unnecessary painting + m_view.notify_server_did_invalidate_content_rect({}, message.content_rect()); +} + +void WebContentClient::handle(const Messages::WebContentClient::DidChangeSelection&) +{ +#ifdef DEBUG_SPAM + dbgln("handle: WebContentClient::DidChangeSelection!"); +#endif + m_view.notify_server_did_change_selection({}); +} + +void WebContentClient::handle(const Messages::WebContentClient::DidLayout& message) +{ +#ifdef DEBUG_SPAM + dbg() << "handle: WebContentClient::DidLayout! content_size=" << message.content_size(); +#endif + m_view.notify_server_did_layout({}, message.content_size()); +} + +void WebContentClient::handle(const Messages::WebContentClient::DidChangeTitle& message) +{ +#ifdef DEBUG_SPAM + dbg() << "handle: WebContentClient::DidChangeTitle! title=" << message.title(); +#endif + m_view.notify_server_did_change_title({}, message.title()); +} + +void WebContentClient::handle(const Messages::WebContentClient::DidRequestScrollIntoView& message) +{ +#ifdef DEBUG_SPAM + dbg() << "handle: WebContentClient::DidRequestScrollIntoView! rect=" << message.rect(); +#endif + m_view.notify_server_did_request_scroll_into_view({}, message.rect()); +} + +void WebContentClient::handle(const Messages::WebContentClient::DidHoverLink& message) +{ +#ifdef DEBUG_SPAM + dbg() << "handle: WebContentClient::DidHoverLink! url=" << message.url(); +#endif + m_view.notify_server_did_hover_link({}, message.url()); +} + +void WebContentClient::handle(const Messages::WebContentClient::DidUnhoverLink&) +{ +#ifdef DEBUG_SPAM + dbgln("handle: WebContentClient::DidUnhoverLink!"); +#endif + m_view.notify_server_did_unhover_link({}); +} + +void WebContentClient::handle(const Messages::WebContentClient::DidClickLink& message) +{ + m_view.notify_server_did_click_link({}, message.url(), message.target(), message.modifiers()); +} + +void WebContentClient::handle(const Messages::WebContentClient::DidMiddleClickLink& message) +{ + m_view.notify_server_did_middle_click_link({}, message.url(), message.target(), message.modifiers()); +} + +void WebContentClient::handle(const Messages::WebContentClient::DidStartLoading& message) +{ + m_view.notify_server_did_start_loading({}, message.url()); +} + +void WebContentClient::handle(const Messages::WebContentClient::DidRequestContextMenu& message) +{ + m_view.notify_server_did_request_context_menu({}, message.content_position()); +} + +void WebContentClient::handle(const Messages::WebContentClient::DidRequestLinkContextMenu& message) +{ + m_view.notify_server_did_request_link_context_menu({}, message.content_position(), message.url(), message.target(), message.modifiers()); +} + +OwnPtr<Messages::WebContentClient::DidRequestAlertResponse> WebContentClient::handle(const Messages::WebContentClient::DidRequestAlert& message) +{ + m_view.notify_server_did_request_alert({}, message.message()); + return make<Messages::WebContentClient::DidRequestAlertResponse>(); +} + +} diff --git a/Userland/Libraries/LibWeb/WebContentClient.h b/Userland/Libraries/LibWeb/WebContentClient.h new file mode 100644 index 0000000000..2078882578 --- /dev/null +++ b/Userland/Libraries/LibWeb/WebContentClient.h @@ -0,0 +1,68 @@ +/* + * 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 <AK/HashMap.h> +#include <LibIPC/ServerConnection.h> +#include <WebContent/WebContentClientEndpoint.h> +#include <WebContent/WebContentServerEndpoint.h> + +namespace Web { + +class OutOfProcessWebView; + +class WebContentClient + : public IPC::ServerConnection<WebContentClientEndpoint, WebContentServerEndpoint> + , public WebContentClientEndpoint { + C_OBJECT(WebContentClient); + +public: + virtual void handshake() override; + +private: + WebContentClient(OutOfProcessWebView&); + + virtual void handle(const Messages::WebContentClient::DidPaint&) override; + virtual void handle(const Messages::WebContentClient::DidFinishLoading&) override; + virtual void handle(const Messages::WebContentClient::DidInvalidateContentRect&) override; + virtual void handle(const Messages::WebContentClient::DidChangeSelection&) override; + virtual void handle(const Messages::WebContentClient::DidLayout&) override; + virtual void handle(const Messages::WebContentClient::DidChangeTitle&) override; + virtual void handle(const Messages::WebContentClient::DidRequestScrollIntoView&) override; + virtual void handle(const Messages::WebContentClient::DidHoverLink&) override; + virtual void handle(const Messages::WebContentClient::DidUnhoverLink&) override; + virtual void handle(const Messages::WebContentClient::DidClickLink&) override; + virtual void handle(const Messages::WebContentClient::DidMiddleClickLink&) override; + virtual void handle(const Messages::WebContentClient::DidStartLoading&) override; + virtual void handle(const Messages::WebContentClient::DidRequestContextMenu&) override; + virtual void handle(const Messages::WebContentClient::DidRequestLinkContextMenu&) override; + virtual OwnPtr<Messages::WebContentClient::DidRequestAlertResponse> handle(const Messages::WebContentClient::DidRequestAlert&) override; + + OutOfProcessWebView& m_view; +}; + +} diff --git a/Userland/Libraries/LibWeb/WebViewHooks.h b/Userland/Libraries/LibWeb/WebViewHooks.h new file mode 100644 index 0000000000..198526e019 --- /dev/null +++ b/Userland/Libraries/LibWeb/WebViewHooks.h @@ -0,0 +1,51 @@ +/* + * 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 <AK/Function.h> +#include <LibGfx/Forward.h> +#include <LibWeb/Forward.h> + +namespace Web { + +class WebViewHooks { +public: + Function<void(const Gfx::IntPoint& screen_position)> on_context_menu_request; + Function<void(const URL&, const String& target, unsigned modifiers)> on_link_click; + Function<void(const URL&, const Gfx::IntPoint& screen_position)> on_link_context_menu_request; + Function<void(const URL&, const Gfx::IntPoint& screen_position, const Gfx::ShareableBitmap&)> on_image_context_menu_request; + Function<void(const URL&, const String& target, unsigned modifiers)> on_link_middle_click; + Function<void(const URL&)> on_link_hover; + Function<void(const String&)> on_title_change; + Function<void(const URL&)> on_load_start; + Function<void(const URL&)> on_load_finish; + Function<void(const Gfx::Bitmap&)> on_favicon_change; + Function<void(const URL&)> on_url_drop; + Function<void(DOM::Document*)> on_set_document; +}; + +} |