1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 19:27:35 +00:00

Libraries: Move to Userland/Libraries/

This commit is contained in:
Andreas Kling 2021-01-12 12:17:30 +01:00
parent dc28c07fa5
commit 13d7c09125
1857 changed files with 266 additions and 274 deletions

View file

@ -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()
{
}
}
}

View file

@ -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;
};
}
}

View file

@ -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);
}
}

View file

@ -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&);
}

View file

@ -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));
}
}
}

View file

@ -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&);
}

View file

@ -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();
}
}
}

View file

@ -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);
};
}
}

View file

@ -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());
}
}
}

View file

@ -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);
};
}
}

View file

@ -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));
}
}

View file

@ -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&);
}
}

View file

@ -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()));
}
}

View file

@ -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"; }
};
}

View file

@ -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());
}
}

View file

@ -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);
};
}

View file

@ -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));
}
}

View file

@ -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&);
}

View file

@ -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()
{
}
}

View file

@ -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;
};
}

View file

@ -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()));
}
}
}

View file

@ -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 };
};
}
}

View file

@ -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();
}
}
}

View file

@ -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();
}
}

View file

@ -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)
{
}
};
}

View file

@ -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()));
}
}

View file

@ -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"; }
};
}

View file

@ -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());
}
}

View file

@ -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);
};
}

View file

@ -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());
}
}

View file

@ -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&);
}

View file

@ -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)

View file

@ -0,0 +1,3 @@
DefaultStyleSheetSource.cpp
PropertyID.cpp
PropertyID.h

View file

@ -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; }
};
}

View file

@ -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;
}

View file

@ -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"
]

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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() };
};
}

View file

@ -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);
}
}

View file

@ -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&);
}

View file

@ -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"
}
}

View file

@ -0,0 +1,3 @@
table {
text-align: left;
}

View file

@ -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;
}
}

View file

@ -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;
};
}

View file

@ -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);
}
}

View file

@ -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&);
}

View file

@ -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()
{
}
}

View file

@ -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;
};
}

View file

@ -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;
});
}
}

View file

@ -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;
};
}

View file

@ -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 {};
}
}
}

View file

@ -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;
};
}

View file

@ -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;
}
}

View file

@ -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;
};
}

View file

@ -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()
{
}
}

View file

@ -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;
};
}

View file

@ -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()
{
}
}

View file

@ -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;
};
}

View file

@ -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)
{
}
}

View file

@ -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;
};
}

View file

@ -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({});
}
}

View file

@ -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;
}
}

View file

@ -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)

View file

@ -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());
}

View file

@ -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());
}

View file

@ -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());
}

View file

@ -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());
}

View file

@ -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());
}

View file

@ -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;
};
}

View file

@ -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()
{
}
}

View file

@ -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;
};
}

View file

@ -0,0 +1,9 @@
interface CharacterData : Node {
attribute DOMString data;
readonly attribute unsigned long length;
readonly attribute Element? nextElementSibling;
readonly attribute Element? previousElementSibling;
};

View file

@ -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()
{
}
}

View file

@ -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"; }
};
}

View file

@ -0,0 +1,3 @@
interface Comment : CharacterData {
};

View file

@ -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;
}
}

View file

@ -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;
};
}

View file

@ -0,0 +1,7 @@
interface DOMImplementation {
Document createHTMLDocument(optional DOMString title);
boolean hasFeature();
};

View file

@ -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 thiss 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));
}
}

View file

@ -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 };
};
}

View file

@ -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;
};

View file

@ -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()
{
}
}

View file

@ -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;
};
}

View file

@ -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);
};

View file

@ -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()
{
}
}

View file

@ -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;
};
}

View file

@ -0,0 +1,7 @@
interface DocumentType : Node {
readonly attribute DOMString name;
readonly attribute DOMString publicId;
readonly attribute DOMString systemId;
};

View file

@ -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("&amp;");
// FIXME: also replace U+00A0 NO-BREAK SPACE with &nbsp;
else if (ch == '"' && attribute_mode)
builder.append("&quot;");
else if (ch == '<' && !attribute_mode)
builder.append("&lt;");
else if (ch == '>' && !attribute_mode)
builder.append("&gt;");
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;
}
}

View file

@ -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();
}
}

View file

@ -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;
};

View file

@ -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));
}
}

View file

@ -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_);
}

View file

@ -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;
}
}

View file

@ -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();
};
}

View file

@ -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;
};

View file

@ -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 lefts 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 targets 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();
}
}

View file

@ -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);
};
}

View file

@ -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();
}
}

View file

@ -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 };
};
}

Some files were not shown because too many files have changed in this diff Show more