Everywhere: Implement persistence of localStorage using sqlite

This change follows the pattern of our cookies persistence
implementation: the "browser" process is responsible for interacting
with the sqlite database, and WebContent communicates all storage
operations via IPC.

The new database table uses (storage_endpoint, storage_key, bottle_key)
as the primary key. This design follows concepts from the
https://storage.spec.whatwg.org/ and is intended to support reuse of the
persistence layer for other APIs (e.g., CacheStorage, IndexedDB). For
now, `storage_endpoint` is always "localStorage", `storage_key` is the
website's origin, and `bottle_key` is the name of the localStorage key.
This commit is contained in:
Aliaksandr Kalenik 2025-06-08 23:35:46 +02:00 committed by Alexander Kalenik
parent f53559cb55
commit 84b9224121
Notes: github-actions[bot] 2025-06-12 15:05:54 +00:00
24 changed files with 694 additions and 118 deletions

View file

@ -578,6 +578,54 @@ void PageClient::page_did_expire_cookies_with_time_offset(AK::Duration offset)
client().async_did_expire_cookies_with_time_offset(offset);
}
Optional<String> PageClient::page_did_request_storage_item(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key, String const& bottle_key)
{
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidRequestStorageItem>(storage_endpoint, storage_key, bottle_key);
if (!response) {
dbgln("WebContent client disconnected during DidRequestStorageItem. Exiting peacefully.");
exit(0);
}
return response->take_value();
}
WebView::StorageOperationError PageClient::page_did_set_storage_item(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key, String const& bottle_key, String const& value)
{
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidSetStorageItem>(storage_endpoint, storage_key, bottle_key, value);
if (!response) {
dbgln("WebContent client disconnected during DidSetStorageItem. Exiting peacefully.");
exit(0);
}
return response->error();
}
void PageClient::page_did_remove_storage_item(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key, String const& bottle_key)
{
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidRemoveStorageItem>(storage_endpoint, storage_key, bottle_key);
if (!response) {
dbgln("WebContent client disconnected during DidRemoveStorageItem. Exiting peacefully.");
exit(0);
}
}
Vector<String> PageClient::page_did_request_storage_keys(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key)
{
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidRequestStorageKeys>(storage_endpoint, storage_key);
if (!response) {
dbgln("WebContent client disconnected during DidRequestStorageKeys. Exiting peacefully.");
exit(0);
}
return response->take_keys();
}
void PageClient::page_did_clear_storage(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key)
{
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidClearStorage>(storage_endpoint, storage_key);
if (!response) {
dbgln("WebContent client disconnected during DidClearStorage. Exiting peacefully.");
exit(0);
}
}
void PageClient::page_did_update_resource_count(i32 count_waiting)
{
client().async_did_update_resource_count(m_id, count_waiting);

View file

@ -14,7 +14,9 @@
#include <LibWeb/HTML/FileFilter.h>
#include <LibWeb/Page/Page.h>
#include <LibWeb/PixelUnits.h>
#include <LibWeb/StorageAPI/StorageEndpoint.h>
#include <LibWebView/Forward.h>
#include <LibWebView/StorageOperationError.h>
#include <WebContent/BackingStoreManager.h>
#include <WebContent/Forward.h>
@ -156,6 +158,11 @@ private:
virtual void page_did_set_cookie(URL::URL const&, Web::Cookie::ParsedCookie const&, Web::Cookie::Source) override;
virtual void page_did_update_cookie(Web::Cookie::Cookie const&) override;
virtual void page_did_expire_cookies_with_time_offset(AK::Duration) override;
virtual Optional<String> page_did_request_storage_item(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key, String const& bottle_key) override;
virtual WebView::StorageOperationError page_did_set_storage_item(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key, String const& bottle_key, String const& value) override;
virtual void page_did_remove_storage_item(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key, String const& bottle_key) override;
virtual Vector<String> page_did_request_storage_keys(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key) override;
virtual void page_did_clear_storage(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key) override;
virtual void page_did_update_resource_count(i32) override;
virtual NewWebViewResult page_did_request_new_web_view(Web::HTML::ActivateTab, Web::HTML::WebViewHints, Web::HTML::TokenizedFeature::NoOpener) override;
virtual void page_did_request_activate_tab() override;

View file

@ -20,6 +20,8 @@
#include <LibWebView/Attribute.h>
#include <LibWebView/ConsoleOutput.h>
#include <LibWebView/DOMNodeProperties.h>
#include <LibWeb/StorageAPI/StorageEndpoint.h>
#include <LibWebView/StorageOperationError.h>
#include <LibWebView/Mutation.h>
#include <LibWebView/PageInfo.h>
#include <LibWebView/ProcessHandle.h>
@ -76,6 +78,11 @@ endpoint WebContentClient
did_set_cookie(URL::URL url, Web::Cookie::ParsedCookie cookie, Web::Cookie::Source source) => ()
did_update_cookie(Web::Cookie::Cookie cookie) =|
did_expire_cookies_with_time_offset(AK::Duration offset) =|
did_request_storage_item(Web::StorageAPI::StorageEndpointType storage_endpoint, String storage_key, String bottle_key) => (Optional<String> value)
did_set_storage_item(Web::StorageAPI::StorageEndpointType storage_endpoint, String storage_key, String bottle_key, String value) => (WebView::StorageOperationError error)
did_remove_storage_item(Web::StorageAPI::StorageEndpointType storage_endpoint, String storage_key, String bottle_key) => ()
did_request_storage_keys(Web::StorageAPI::StorageEndpointType storage_endpoint, String storage_key) => (Vector<String> keys)
did_clear_storage(Web::StorageAPI::StorageEndpointType storage_endpoint, String storage_key) => ()
did_update_resource_count(u64 page_id, i32 count_waiting) =|
did_request_new_web_view(u64 page_id, Web::HTML::ActivateTab activate_tab, Web::HTML::WebViewHints hints, Optional<u64> page_index) => (String handle)
did_request_activate_tab(u64 page_id) =|