diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObject.cpp b/Userland/Libraries/LibWeb/Bindings/WindowObject.cpp index 62c701b3b75916..54a8de9598095d 100644 --- a/Userland/Libraries/LibWeb/Bindings/WindowObject.cpp +++ b/Userland/Libraries/LibWeb/Bindings/WindowObject.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021, Andreas Kling + * Copyright (c) 2020-2022, Andreas Kling * Copyright (c) 2021, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -110,6 +112,8 @@ void WindowObject::initialize_global_object() define_direct_property("CSS", heap().allocate(*this, *this), 0); + define_native_accessor("localStorage", local_storage_getter, {}, attr); + // Legacy define_native_accessor("event", event_getter, event_setter, JS::Attribute::Enumerable); @@ -646,6 +650,13 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::screen_y_getter) return JS::Value(impl->screen_y()); } +JS_DEFINE_NATIVE_FUNCTION(WindowObject::local_storage_getter) +{ + auto* impl = TRY(impl_from(vm, global_object)); + // FIXME: localStorage may throw. We have to deal with that here. + return wrap(global_object, *impl->local_storage()); +} + #define __ENUMERATE(attribute, event_name) \ JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_getter) \ { \ diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObject.h b/Userland/Libraries/LibWeb/Bindings/WindowObject.h index b89a450dcfdf12..866d89a4071a90 100644 --- a/Userland/Libraries/LibWeb/Bindings/WindowObject.h +++ b/Userland/Libraries/LibWeb/Bindings/WindowObject.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021, Andreas Kling + * Copyright (c) 2020-2022, Andreas Kling * Copyright (c) 2021, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause @@ -94,6 +94,8 @@ class WindowObject JS_DECLARE_NATIVE_FUNCTION(screen_left_getter); JS_DECLARE_NATIVE_FUNCTION(screen_top_getter); + JS_DECLARE_NATIVE_FUNCTION(local_storage_getter); + JS_DECLARE_NATIVE_FUNCTION(alert); JS_DECLARE_NATIVE_FUNCTION(confirm); JS_DECLARE_NATIVE_FUNCTION(prompt); diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h index 16270225c35677..deb454f5edbf1d 100644 --- a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h +++ b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Andreas Kling + * Copyright (c) 2021-2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -263,6 +263,8 @@ #include #include #include +#include +#include #include #include #include @@ -424,6 +426,7 @@ ADD_WINDOW_OBJECT_INTERFACE(Screen) \ ADD_WINDOW_OBJECT_INTERFACE(Selection) \ ADD_WINDOW_OBJECT_INTERFACE(ShadowRoot) \ + ADD_WINDOW_OBJECT_INTERFACE(Storage) \ ADD_WINDOW_OBJECT_INTERFACE(StyleSheet) \ ADD_WINDOW_OBJECT_INTERFACE(StyleSheetList) \ ADD_WINDOW_OBJECT_INTERFACE(SubmitEvent) \ diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index d567364dce4d4c..762a2c96ac2219 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -196,6 +196,7 @@ set(SOURCES HTML/Scripting/ExceptionReporter.cpp HTML/Scripting/Script.cpp HTML/Scripting/WindowEnvironmentSettingsObject.cpp + HTML/Storage.cpp HTML/SyntaxHighlighter/SyntaxHighlighter.cpp HTML/TagNames.cpp HTML/TextMetrics.cpp @@ -493,6 +494,7 @@ libweb_js_wrapper(HTML/MessageEvent) libweb_js_wrapper(HTML/MessagePort) libweb_js_wrapper(HTML/PageTransitionEvent) libweb_js_wrapper(HTML/PromiseRejectionEvent) +libweb_js_wrapper(HTML/Storage) libweb_js_wrapper(HTML/SubmitEvent) libweb_js_wrapper(HTML/TextMetrics) libweb_js_wrapper(HTML/WebSocket) diff --git a/Userland/Libraries/LibWeb/DOM/Window.cpp b/Userland/Libraries/LibWeb/DOM/Window.cpp index 130b694bdfdce8..fbdfac53c19493 100644 --- a/Userland/Libraries/LibWeb/DOM/Window.cpp +++ b/Userland/Libraries/LibWeb/DOM/Window.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021, Andreas Kling + * Copyright (c) 2020-2022, Andreas Kling * Copyright (c) 2021, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -438,4 +439,15 @@ Selection::Selection* Window::get_selection() return nullptr; } +// https://html.spec.whatwg.org/multipage/webstorage.html#dom-localstorage +RefPtr Window::local_storage() +{ + // FIXME: Implement according to spec. + + static HashMap> local_storage_per_origin; + return local_storage_per_origin.ensure(associated_document().origin(), [] { + return HTML::Storage::create(); + }); +} + } diff --git a/Userland/Libraries/LibWeb/DOM/Window.h b/Userland/Libraries/LibWeb/DOM/Window.h index f4ff0187ccb307..99fa996d6ea322 100644 --- a/Userland/Libraries/LibWeb/DOM/Window.h +++ b/Userland/Libraries/LibWeb/DOM/Window.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021, Andreas Kling + * Copyright (c) 2020-2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -98,6 +98,8 @@ class Window final Selection::Selection* get_selection(); + RefPtr local_storage(); + private: explicit Window(Document&); diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index f0da95d503cb06..48064f4e6312d9 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021, Andreas Kling + * Copyright (c) 2020-2022, Andreas Kling * Copyright (c) 2021, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause @@ -213,6 +213,7 @@ class MessageEvent; class MessagePort; class PageTransitionEvent; class PromiseRejectionEvent; +class Storage; class SubmitEvent; class TextMetrics; class WebSocket; @@ -440,6 +441,7 @@ class RangeWrapper; class ResizeObserverWrapper; class ScreenWrapper; class SelectionWrapper; +class StorageWrapper; class StyleSheetListWrapper; class StyleSheetWrapper; class SubmitEventWrapper; diff --git a/Userland/Libraries/LibWeb/HTML/Storage.cpp b/Userland/Libraries/LibWeb/HTML/Storage.cpp new file mode 100644 index 00000000000000..533c95b9281964 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Storage.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2022, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::HTML { + +NonnullRefPtr Storage::create() +{ + return adopt_ref(*new Storage); +} + +Storage::Storage() +{ +} + +Storage::~Storage() +{ +} + +// https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-length +size_t Storage::length() const +{ + // The length getter steps are to return this's map's size. + return m_map.size(); +} + +// https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-key +String Storage::key(size_t index) +{ + // 1. If index is greater than or equal to this's map's size, then return null. + if (index >= m_map.size()) + return {}; + + // 2. Let keys be the result of running get the keys on this's map. + auto keys = m_map.keys(); + + // 3. Return keys[index]. + return keys[index]; +} + +// https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-getitem +String Storage::get_item(String const& key) const +{ + // 1. If this's map[key] does not exist, then return null. + auto it = m_map.find(key); + if (it == m_map.end()) + return {}; + + // 2. Return this's map[key]. + return it->value; +} + +// https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-setitem +DOM::ExceptionOr Storage::set_item(String const& key, String const& value) +{ + // 1. Let oldValue be null. + String old_value; + + // 2. Let reorder be true. + bool reorder = true; + + // 3. If this's map[key] exists: + if (auto it = m_map.find(key); it != m_map.end()) { + // 1. Set oldValue to this's map[key]. + old_value = it->value; + + // 2. If oldValue is value, then return. + if (old_value == value) + return {}; + + // 3. Set reorder to false. + reorder = false; + } + + // FIXME: 4. If value cannot be stored, then throw a "QuotaExceededError" DOMException exception. + + // 5. Set this's map[key] to value. + m_map.set(key, value); + + // 6. If reorder is true, then reorder this. + if (reorder) + this->reorder(); + + // 7. Broadcast this with key, oldValue, and value. + broadcast(key, old_value, value); + + return {}; +} + +// https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-removeitem +void Storage::remove_item(String const& key) +{ + // 1. If this's map[key] does not exist, then return null. + // FIXME: Return null? + auto it = m_map.find(key); + if (it == m_map.end()) + return; + + // 2. Set oldValue to this's map[key]. + auto old_value = it->value; + + // 3. Remove this's map[key]. + m_map.remove(it); + + // 4. Reorder this. + reorder(); + + // 5. Broadcast this with key, oldValue, and null. + broadcast(key, old_value, {}); +} + +// https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-clear +void Storage::clear() +{ + // 1. Clear this's map. + m_map.clear(); + + // 2. Broadcast this with null, null, and null. + broadcast({}, {}, {}); +} + +// https://html.spec.whatwg.org/multipage/webstorage.html#concept-storage-reorder +void Storage::reorder() +{ + // To reorder a Storage object storage, reorder storage's map's entries in an implementation-defined manner. + // NOTE: This basically means that we're not required to maintain any particular iteration order. +} + +// https://html.spec.whatwg.org/multipage/webstorage.html#concept-storage-broadcast +void Storage::broadcast(String const& key, String const& old_value, String const& new_value) +{ + (void)key; + (void)old_value; + (void)new_value; + // FIXME: Implement. +} + +Vector Storage::supported_property_names() const +{ + // The supported property names on a Storage object storage are the result of running get the keys on storage's map. + return m_map.keys(); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/Storage.h b/Userland/Libraries/LibWeb/HTML/Storage.h new file mode 100644 index 00000000000000..d8cf2c63e74df4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Storage.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Web::HTML { + +class Storage + : public RefCounted + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::StorageWrapper; + + static NonnullRefPtr create(); + ~Storage(); + + size_t length() const; + String key(size_t index); + String get_item(String const& key) const; + DOM::ExceptionOr set_item(String const& key, String const& value); + void remove_item(String const& key); + void clear(); + + Vector supported_property_names() const; + +private: + Storage(); + + void reorder(); + void broadcast(String const& key, String const& old_value, String const& new_value); + + OrderedHashMap m_map; +}; + +} + +namespace Web::Bindings { + +StorageWrapper* wrap(JS::GlobalObject&, HTML::Storage&); + +} diff --git a/Userland/Libraries/LibWeb/HTML/Storage.idl b/Userland/Libraries/LibWeb/HTML/Storage.idl new file mode 100644 index 00000000000000..4c1005e17ad8b7 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/Storage.idl @@ -0,0 +1,11 @@ +[Exposed=Window] +interface Storage { + + readonly attribute unsigned long length; + DOMString? key(unsigned long index); + getter DOMString? getItem(DOMString key); + setter undefined setItem(DOMString key, DOMString value); + deleter undefined removeItem(DOMString key); + undefined clear(); + +};