diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index 83f6c9bd71..17236cf335 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -48,8 +48,6 @@ Interpreter::Interpreter() m_array_prototype = heap().allocate(); m_error_prototype = heap().allocate(); m_date_prototype = heap().allocate(); - - m_global_object = heap().allocate(); } Interpreter::~Interpreter() diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index 2902cabdb8..5145d6b103 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -71,6 +71,13 @@ public: Interpreter(); ~Interpreter(); + template + void initialize_global_object(Args&&... args) + { + ASSERT(!m_global_object); + m_global_object = heap().allocate(forward(args)...); + } + Value run(const Statement&, Vector = {}, ScopeType = ScopeType::Block); Object& global_object() { return *m_global_object; } diff --git a/Libraries/LibJS/Runtime/GlobalObject.h b/Libraries/LibJS/Runtime/GlobalObject.h index c5efc058b9..ab2898713d 100644 --- a/Libraries/LibJS/Runtime/GlobalObject.h +++ b/Libraries/LibJS/Runtime/GlobalObject.h @@ -4,7 +4,7 @@ namespace JS { -class GlobalObject final : public Object { +class GlobalObject : public Object { public: explicit GlobalObject(); virtual ~GlobalObject() override; diff --git a/Libraries/LibWeb/Bindings/WindowObject.cpp b/Libraries/LibWeb/Bindings/WindowObject.cpp new file mode 100644 index 0000000000..ca669ee772 --- /dev/null +++ b/Libraries/LibWeb/Bindings/WindowObject.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Web { +namespace Bindings { + +WindowObject::WindowObject(Window& impl) + : m_impl(impl) +{ + put_native_property("document", document_getter, document_setter); + put_native_function("alert", alert); + put_native_function("setInterval", set_interval); + put_native_function("requestAnimationFrame", request_animation_frame); + put_native_function("cancelAnimationFrame", cancel_animation_frame); +} + +WindowObject::~WindowObject() +{ +} + +static Window* impl_from(JS::Interpreter& interpreter) +{ + auto* this_object = interpreter.this_value().to_object(interpreter.heap()); + if (!this_object) { + dbg() << "this_object is null"; + ASSERT_NOT_REACHED(); + return nullptr; + } + if (StringView("WindowObject") != this_object->class_name()) { + interpreter.throw_exception("TypeError", "That's not a WindowObject, bro."); + dbg() << "this_object class_name is '" << this_object->class_name() << "'"; + return nullptr; + } + return &static_cast(this_object)->impl(); +} + +JS::Value WindowObject::alert(JS::Interpreter& interpreter) +{ + dbg() << "alert entry"; + auto* impl = impl_from(interpreter); + if (!impl) + return {}; + dbg() << "alert2 entry"; + auto& arguments = interpreter.call_frame().arguments; + if (arguments.size() < 1) + return {}; + impl->alert(arguments[0].to_string()); + return {}; +} + +JS::Value WindowObject::set_interval(JS::Interpreter& interpreter) +{ + auto* impl = impl_from(interpreter); + if (!impl) + return {}; + auto& arguments = interpreter.call_frame().arguments; + if (arguments.size() < 2) + return {}; + auto* callback_object = arguments[0].to_object(interpreter.heap()); + if (!callback_object) + return {}; + if (!callback_object->is_function()) + return interpreter.throw_exception("TypeError", "Not a function"); + impl->set_interval(*static_cast(callback_object), arguments[1].to_i32()); + return {}; +} + +JS::Value WindowObject::request_animation_frame(JS::Interpreter& interpreter) +{ + auto* impl = impl_from(interpreter); + if (!impl) + return {}; + auto& arguments = interpreter.call_frame().arguments; + if (arguments.size() < 1) + return {}; + auto* callback_object = arguments[0].to_object(interpreter.heap()); + if (!callback_object) + return {}; + if (!callback_object->is_function()) + return interpreter.throw_exception("TypeError", "Not a function"); + return JS::Value(impl->request_animation_frame(*static_cast(callback_object))); +} + + +JS::Value WindowObject::cancel_animation_frame(JS::Interpreter& interpreter) +{ + auto* impl = impl_from(interpreter); + if (!impl) + return {}; + auto& arguments = interpreter.call_frame().arguments; + if (arguments.size() < 1) + return {}; + impl->cancel_animation_frame(arguments[0].to_i32()); + return {}; +} + +JS::Value WindowObject::document_getter(JS::Interpreter& interpreter) +{ + auto* impl = impl_from(interpreter); + if (!impl) + return {}; + return wrap(interpreter.heap(), impl->document()); +} + +void WindowObject::document_setter(JS::Interpreter&, JS::Value) +{ + // FIXME: Figure out what we should do here. Just ignore attempts to set window.document for now. +} + +} +} diff --git a/Libraries/LibWeb/Bindings/WindowObject.h b/Libraries/LibWeb/Bindings/WindowObject.h new file mode 100644 index 0000000000..f47c9e5ca1 --- /dev/null +++ b/Libraries/LibWeb/Bindings/WindowObject.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include + +namespace Web { +namespace Bindings { + +class WindowObject : public JS::GlobalObject { +public: + explicit WindowObject(Window&); + virtual ~WindowObject() override; + + Window& impl() { return *m_impl; } + const Window& impl() const { return *m_impl; } + +private: + virtual const char* class_name() const override { return "WindowObject"; } + + static JS::Value document_getter(JS::Interpreter&); + static void document_setter(JS::Interpreter&, JS::Value); + + static JS::Value alert(JS::Interpreter&); + static JS::Value set_interval(JS::Interpreter&); + static JS::Value request_animation_frame(JS::Interpreter&); + static JS::Value cancel_animation_frame(JS::Interpreter&); + + NonnullRefPtr m_impl; +}; + +} +} diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 555124fb27..9dee2a6e02 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +59,7 @@ namespace Web { Document::Document() : ParentNode(*this, NodeType::DOCUMENT_NODE) , m_style_resolver(make(*this)) + , m_window(Window::create_with_document(*this)) { m_style_update_timer = Core::Timer::construct(); m_style_update_timer->set_single_shot(true); @@ -359,65 +362,7 @@ JS::Interpreter& Document::interpreter() { if (!m_interpreter) { m_interpreter = make(); - - m_interpreter->global_object().put_native_function("alert", [](JS::Interpreter& interpreter) -> JS::Value { - auto& arguments = interpreter.call_frame().arguments; - if (arguments.size() < 1) - return JS::js_undefined(); - GUI::MessageBox::show(arguments[0].to_string(), "Alert", GUI::MessageBox::Type::Information); - return JS::js_undefined(); - }); - - m_interpreter->global_object().put_native_function("setInterval", [this](JS::Interpreter& interpreter) -> JS::Value { - auto& arguments = interpreter.call_frame().arguments; - if (arguments.size() < 2) - return JS::js_undefined(); - ASSERT(arguments[0].is_object()); - ASSERT(arguments[0].as_object()->is_function()); - auto callback = make_handle(const_cast(arguments[0].as_object())); - - // FIXME: This timer should not be leaked! It should also be removable with clearInterval()! - (void)Core::Timer::construct( - arguments[1].to_i32(), [this, callback] { - auto* function = const_cast(static_cast(callback.cell())); - m_interpreter->call(function); - }) - .leak_ref(); - - return JS::js_undefined(); - }); - - m_interpreter->global_object().put_native_function("requestAnimationFrame", [this](JS::Interpreter& interpreter) -> JS::Value { - auto& arguments = interpreter.call_frame().arguments; - if (arguments.size() < 1) - return JS::js_undefined(); - ASSERT(arguments[0].is_object()); - ASSERT(arguments[0].as_object()->is_function()); - auto callback = make_handle(const_cast(arguments[0].as_object())); - // FIXME: Don't hand out raw DisplayLink ID's to JavaScript! - i32 link_id = GUI::DisplayLink::register_callback([this, callback](i32 link_id) { - auto* function = const_cast(static_cast(callback.cell())); - m_interpreter->call(function); - GUI::DisplayLink::unregister_callback(link_id); - }); - return JS::Value(link_id); - }); - - m_interpreter->global_object().put_native_function("cancelAnimationFrame", [](JS::Interpreter& interpreter) -> JS::Value { - auto& arguments = interpreter.call_frame().arguments; - if (arguments.size() < 1) - return JS::js_undefined(); - // FIXME: We should not be passing untrusted numbers to DisplayLink::unregistered_callback()! - GUI::DisplayLink::unregister_callback(arguments[0].to_i32()); - return JS::js_undefined(); - }); - - m_interpreter->global_object().put_native_property( - "document", - [this](JS::Interpreter&) { - return wrap(m_interpreter->heap(), *this); - }, - nullptr); + m_interpreter->initialize_global_object(*m_window); } return *m_interpreter; } diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index df183081a7..a578ff2152 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -42,15 +42,6 @@ namespace Web { -class Frame; -class HTMLBodyElement; -class HTMLHtmlElement; -class HTMLHeadElement; -class LayoutDocument; -class LayoutNode; -class StyleResolver; -class StyleSheet; - class Document : public ParentNode , public NonElementParentNode { @@ -140,6 +131,8 @@ private: WeakPtr m_frame; URL m_url; + RefPtr m_window; + RefPtr m_layout_root; Optional m_link_color; diff --git a/Libraries/LibWeb/DOM/Window.cpp b/Libraries/LibWeb/DOM/Window.cpp new file mode 100644 index 0000000000..fb806ce43c --- /dev/null +++ b/Libraries/LibWeb/DOM/Window.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +namespace Web { + +NonnullRefPtr Window::create_with_document(Document& document) +{ + return adopt(*new Window(document)); +} + +Window::Window(Document& document) + : m_document(document) +{ +} + +Window::~Window() +{ +} + +void Window::alert(const String& message) +{ + GUI::MessageBox::show(message, "Alert", GUI::MessageBox::Type::Information); +} + +void Window::set_interval(JS::Function& callback, i32 interval) +{ + // FIXME: This leaks the interval timer and makes it unstoppable. + (void)Core::Timer::construct(interval, [handle = make_handle(&callback)] { + auto* function = const_cast(static_cast(handle.cell())); + auto& interpreter = function->interpreter(); + interpreter.call(function); + }).leak_ref(); +} + +i32 Window::request_animation_frame(JS::Function& callback) +{ + i32 link_id = GUI::DisplayLink::register_callback([handle = make_handle(&callback)](i32 link_id) { + auto* function = const_cast(static_cast(handle.cell())); + auto& interpreter = function->interpreter(); + interpreter.call(function); + GUI::DisplayLink::unregister_callback(link_id); + }); + + // FIXME: Don't hand out raw DisplayLink ID's to JavaScript! + return link_id; +} + +void Window::cancel_animation_frame(i32 id) +{ + // FIXME: We should not be passing untrusted numbers to DisplayLink::unregister_callback()! + GUI::DisplayLink::unregister_callback(id); +} + +} diff --git a/Libraries/LibWeb/DOM/Window.h b/Libraries/LibWeb/DOM/Window.h new file mode 100644 index 0000000000..a3ec299ad6 --- /dev/null +++ b/Libraries/LibWeb/DOM/Window.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Web { + +class Window : public RefCounted { +public: + static NonnullRefPtr create_with_document(Document&); + ~Window(); + + const Document& document() const { return m_document; } + Document& document() { return m_document; } + + void alert(const String&); + i32 request_animation_frame(JS::Function&); + void cancel_animation_frame(i32); + void set_interval(JS::Function&, i32); + +private: + explicit Window(Document&); + + Document& m_document; +}; + +} diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 0403db9b97..230c112208 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -35,15 +35,21 @@ class Event; class EventListener; class EventTarget; class Frame; +class HTMLBodyElement; class HTMLCanvasElement; class HTMLElement; +class HTMLHeadElement; +class HTMLHtmlElement; class HtmlView; +class LayoutDocument; class LayoutNode; class MouseEvent; class Node; class Selector; +class StyleResolver; class StyleRule; class StyleSheet; +class Window; namespace Bindings { @@ -56,6 +62,7 @@ class EventTargetWrapper; class HTMLCanvasElementWrapper; class MouseEventWrapper; class NodeWrapper; +class WindowObject; class Wrappable; class Wrapper; diff --git a/Libraries/LibWeb/Makefile b/Libraries/LibWeb/Makefile index 5de6b61cd2..f5a1818d5a 100644 --- a/Libraries/LibWeb/Makefile +++ b/Libraries/LibWeb/Makefile @@ -8,6 +8,7 @@ LIBWEB_OBJS = \ Bindings/HTMLCanvasElementWrapper.o \ Bindings/MouseEventWrapper.o \ Bindings/NodeWrapper.o \ + Bindings/WindowObject.o \ Bindings/Wrappable.o \ CSS/DefaultStyleSheetSource.o \ CSS/PropertyID.o \ @@ -51,6 +52,7 @@ LIBWEB_OBJS = \ DOM/Node.o \ DOM/ParentNode.o \ DOM/Text.o \ + DOM/Window.o \ StylePropertiesModel.o \ DOMTreeModel.o \ Dump.o \ diff --git a/Userland/js.cpp b/Userland/js.cpp index f8cfab8db1..4970a870d2 100644 --- a/Userland/js.cpp +++ b/Userland/js.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -133,7 +134,7 @@ void print_value(JS::Value value, HashTable& seen_objects) if (value.is_array()) return print_array(static_cast(value.as_object()), seen_objects); - + if (value.is_object()) { auto* object = value.as_object(); if (object->is_function()) @@ -198,6 +199,7 @@ int main(int argc, char** argv) args_parser.parse(argc, argv); JS::Interpreter interpreter; + interpreter.initialize_global_object(); interpreter.heap().set_should_collect_on_every_allocation(gc_on_every_allocation); interpreter.global_object().put("global", &interpreter.global_object());