From c536f65160319f38fc174e15e2f0af648e10d16a Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Fri, 27 Dec 2024 20:55:08 +1300 Subject: [PATCH] LibWeb/HTML: Implement the broadcast steps for Storage This does not work properly yet as we do not create storage objects in a spec compliant manner. --- Libraries/LibWeb/HTML/Storage.cpp | 76 +++++++++++++++++++++++++++++-- Libraries/LibWeb/HTML/Storage.h | 1 + 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/Libraries/LibWeb/HTML/Storage.cpp b/Libraries/LibWeb/HTML/Storage.cpp index 6420edcc9ab..1a5df493a0f 100644 --- a/Libraries/LibWeb/HTML/Storage.cpp +++ b/Libraries/LibWeb/HTML/Storage.cpp @@ -7,14 +7,24 @@ */ #include +#include #include #include #include +#include +#include namespace Web::HTML { GC_DEFINE_ALLOCATOR(Storage); +static HashTable>& all_storages() +{ + // FIXME: This needs to be stored at the user agent level. + static HashTable> storages; + return storages; +} + GC::Ref Storage::create(JS::Realm& realm, Type type, u64 quota_bytes) { return realm.create(realm, type, quota_bytes); @@ -35,6 +45,8 @@ Storage::Storage(JS::Realm& realm, Type type, u64 quota_bytes) .named_property_setter_has_identifier = true, .named_property_deleter_has_identifier = true, }; + + all_storages().set(*this); } Storage::~Storage() = default; @@ -45,6 +57,11 @@ void Storage::initialize(JS::Realm& realm) WEB_SET_PROTOTYPE_FOR_INTERFACE(Storage); } +void Storage::finalize() +{ + all_storages().remove(*this); +} + // https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-length size_t Storage::length() const { @@ -167,10 +184,61 @@ void Storage::reorder() // https://html.spec.whatwg.org/multipage/webstorage.html#concept-storage-broadcast void Storage::broadcast(Optional const& key, Optional const& old_value, Optional const& new_value) { - (void)key; - (void)old_value; - (void)new_value; - // FIXME: Implement. + auto& realm = this->realm(); + + // 1. Let thisDocument be storage's relevant global object's associated Document. + auto& relevant_global = relevant_global_object(*this); + auto const& this_document = verify_cast(relevant_global).associated_document(); + + // 2. Let url be thisDocument's URL. + auto url = this_document.url().to_string(); + + // 3. Let remoteStorages be all Storage objects excluding storage whose: + GC::RootVector> remote_storages(heap()); + for (auto storage : all_storages()) { + if (storage == this) + continue; + + // * type is storage's type + if (storage->type() != type()) + continue; + + // * relevant settings object's origin is same origin with storage's relevant settings object's origin. + if (!relevant_settings_object(*this).origin().is_same_origin(relevant_settings_object(storage).origin())) + continue; + + // * and, if type is "session", whose relevant settings object's associated Document's node navigable's traversable navigable + // is thisDocument's node navigable's traversable navigable. + if (type() == Type::Session) { + auto& storage_document = *relevant_settings_object(storage).responsible_document(); + + // NOTE: It is possible the remote storage may have not been fully teared down immediately at the point it's document is made inactive. + if (!storage_document.navigable()) + continue; + VERIFY(this_document.navigable()); + + if (storage_document.navigable()->traversable_navigable() != this_document.navigable()->traversable_navigable()) + continue; + } + + remote_storages.append(storage); + } + + // 4. For each remoteStorage of remoteStorages: queue a global task on the DOM manipulation task source given remoteStorage's relevant + // global object to fire an event named storage at remoteStorage's relevant global object, using StorageEvent, with key initialized + // to key, oldValue initialized to oldValue, newValue initialized to newValue, url initialized to url, and storageArea initialized to + // remoteStorage. + for (auto remote_storage : remote_storages) { + queue_global_task(Task::Source::DOMManipulation, relevant_global, GC::create_function(heap(), [&realm, key, old_value, new_value, url, remote_storage] { + StorageEventInit init; + init.key = move(key); + init.old_value = move(old_value); + init.new_value = move(new_value); + init.url = move(url); + init.storage_area = remote_storage; + verify_cast(relevant_global_object(remote_storage)).dispatch_event(StorageEvent::create(realm, EventNames::storage, init)); + })); + } } Vector Storage::supported_property_names() const diff --git a/Libraries/LibWeb/HTML/Storage.h b/Libraries/LibWeb/HTML/Storage.h index 0e06fa0e90b..27c339878f3 100644 --- a/Libraries/LibWeb/HTML/Storage.h +++ b/Libraries/LibWeb/HTML/Storage.h @@ -45,6 +45,7 @@ private: Storage(JS::Realm&, Type, u64 quota_limit); virtual void initialize(JS::Realm&) override; + virtual void finalize() override; // ^PlatformObject virtual Optional item_value(size_t index) const override;