diff --git a/Ladybird/AppKit/Application/ApplicationDelegate.h b/Ladybird/AppKit/Application/ApplicationDelegate.h index e1e349c848..6f127790ee 100644 --- a/Ladybird/AppKit/Application/ApplicationDelegate.h +++ b/Ladybird/AppKit/Application/ApplicationDelegate.h @@ -24,7 +24,7 @@ - (nullable instancetype)init:(Vector)initial_urls newTabPageURL:(URL::URL)new_tab_page_url - withCookieJar:(WebView::CookieJar)cookie_jar + withCookieJar:(NonnullOwnPtr)cookie_jar webContentOptions:(Ladybird::WebContentOptions const&)web_content_options webdriverContentIPCPath:(StringView)webdriver_content_ipc_path; diff --git a/Ladybird/AppKit/Application/ApplicationDelegate.mm b/Ladybird/AppKit/Application/ApplicationDelegate.mm index 59fd1ffd01..af8aae37dd 100644 --- a/Ladybird/AppKit/Application/ApplicationDelegate.mm +++ b/Ladybird/AppKit/Application/ApplicationDelegate.mm @@ -24,7 +24,7 @@ URL::URL m_new_tab_page_url; // This will always be populated, but we cannot have a non-default constructible instance variable. - Optional m_cookie_jar; + OwnPtr m_cookie_jar; Ladybird::WebContentOptions m_web_content_options; Optional m_webdriver_content_ipc_path; @@ -56,7 +56,7 @@ - (instancetype)init:(Vector)initial_urls newTabPageURL:(URL::URL)new_tab_page_url - withCookieJar:(WebView::CookieJar)cookie_jar + withCookieJar:(NonnullOwnPtr)cookie_jar webContentOptions:(Ladybird::WebContentOptions const&)web_content_options webdriverContentIPCPath:(StringView)webdriver_content_ipc_path { diff --git a/Ladybird/Qt/main.cpp b/Ladybird/Qt/main.cpp index cb26385baa..4ebe86c0c4 100644 --- a/Ladybird/Qt/main.cpp +++ b/Ladybird/Qt/main.cpp @@ -185,10 +185,10 @@ ErrorOr serenity_main(Main::Arguments arguments) }; chrome_process.on_new_window = [&](auto const& urls) { - app.new_window(sanitize_urls(urls), cookie_jar, web_content_options, webdriver_content_ipc_path); + app.new_window(sanitize_urls(urls), *cookie_jar, web_content_options, webdriver_content_ipc_path); }; - auto& window = app.new_window(sanitize_urls(raw_urls), cookie_jar, web_content_options, webdriver_content_ipc_path); + auto& window = app.new_window(sanitize_urls(raw_urls), *cookie_jar, web_content_options, webdriver_content_ipc_path); window.setWindowTitle("Ladybird"); if (Ladybird::Settings::the()->is_maximized()) { diff --git a/Userland/Applications/Browser/main.cpp b/Userland/Applications/Browser/main.cpp index 35264c2e8b..be028c8a41 100644 --- a/Userland/Applications/Browser/main.cpp +++ b/Userland/Applications/Browser/main.cpp @@ -217,7 +217,7 @@ ErrorOr serenity_main(Main::Arguments arguments) } auto cookie_jar = TRY(WebView::CookieJar::create(*database)); - auto window = Browser::BrowserWindow::construct(cookie_jar, sanitize_urls(specified_urls), man_file); + auto window = Browser::BrowserWindow::construct(*cookie_jar, sanitize_urls(specified_urls), man_file); chrome_process.on_new_tab = [&](auto const& raw_urls) { open_urls_from_client(*window, raw_urls, NewWindow::No); diff --git a/Userland/Libraries/LibWebView/CookieJar.cpp b/Userland/Libraries/LibWebView/CookieJar.cpp index a463ce0b4b..5192d6d1b6 100644 --- a/Userland/Libraries/LibWebView/CookieJar.cpp +++ b/Userland/Libraries/LibWebView/CookieJar.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -22,7 +21,9 @@ namespace WebView { -ErrorOr CookieJar::create(Database& database) +static constexpr auto DATABASE_SYNCHRONIZATION_TIMER = Duration::from_seconds(30); + +ErrorOr> CookieJar::create(Database& database) { Statements statements {}; @@ -55,38 +56,57 @@ ErrorOr CookieJar::create(Database& database) persistent=? WHERE ((name = ?) AND (domain = ?) AND (path = ?));)#"sv)); - statements.update_cookie_last_access_time = TRY(database.prepare_statement(R"#( - UPDATE Cookies SET last_access_time=? - WHERE ((name = ?) AND (domain = ?) AND (path = ?));)#"sv)); - statements.insert_cookie = TRY(database.prepare_statement("INSERT INTO Cookies VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"sv)); statements.expire_cookie = TRY(database.prepare_statement("DELETE FROM Cookies WHERE (expiry_time < ?);"sv)); - statements.select_cookie = TRY(database.prepare_statement("SELECT * FROM Cookies WHERE ((name = ?) AND (domain = ?) AND (path = ?));"sv)); statements.select_all_cookies = TRY(database.prepare_statement("SELECT * FROM Cookies;"sv)); - return CookieJar { PersistedStorage { database, move(statements) } }; + return adopt_own(*new CookieJar { PersistedStorage { database, statements } }); } -CookieJar CookieJar::create() +NonnullOwnPtr CookieJar::create() { - return CookieJar { TransientStorage {} }; + return adopt_own(*new CookieJar { OptionalNone {} }); } -CookieJar::CookieJar(PersistedStorage storage) - : m_storage(move(storage)) +CookieJar::CookieJar(Optional persisted_storage) + : m_persisted_storage(move(persisted_storage)) { - auto& persisted_storage = m_storage.get(); - persisted_storage.database.execute_statement(persisted_storage.statements.create_table, {}, {}, {}); + if (!m_persisted_storage.has_value()) + return; + + m_persisted_storage->database.execute_statement(m_persisted_storage->statements.create_table, {}, {}, {}); + + // FIXME: Make cookie retrieval lazy so we don't need to retrieve all cookies up front. + auto cookies = m_persisted_storage->select_all_cookies(); + m_transient_storage.set_cookies(move(cookies)); + + m_persisted_storage->synchronization_timer = Core::Timer::create_repeating( + static_cast(DATABASE_SYNCHRONIZATION_TIMER.to_milliseconds()), + [this]() { + auto now = m_transient_storage.purge_expired_cookies(); + m_persisted_storage->database.execute_statement(m_persisted_storage->statements.expire_cookie, {}, {}, {}, now); + + // FIXME: Implement "INSERT OR REPLACE" + for (auto const& it : m_transient_storage.take_inserted_cookies()) + m_persisted_storage->insert_cookie(it.value); + for (auto const& it : m_transient_storage.take_updated_cookies()) + m_persisted_storage->update_cookie(it.value); + }); + m_persisted_storage->synchronization_timer->start(); } -CookieJar::CookieJar(TransientStorage storage) - : m_storage(move(storage)) +CookieJar::~CookieJar() { + if (!m_persisted_storage.has_value()) + return; + + m_persisted_storage->synchronization_timer->stop(); + m_persisted_storage->synchronization_timer->on_timeout(); } String CookieJar::get_cookie(const URL::URL& url, Web::Cookie::Source source) { - purge_expired_cookies(); + m_transient_storage.purge_expired_cookies(); auto domain = canonicalize_domain(url); if (!domain.has_value()) @@ -120,36 +140,31 @@ void CookieJar::set_cookie(const URL::URL& url, Web::Cookie::ParsedCookie const& // however the whole ParsedCookie->Cookie conversion is skipped. void CookieJar::update_cookie(Web::Cookie::Cookie cookie) { - select_cookie_from_database( - move(cookie), + CookieStorageKey key { cookie.name, cookie.domain, cookie.path }; - // 11. If the cookie store contains a cookie with the same name, domain, and path as the newly created cookie: - [this](auto& cookie, auto old_cookie) { - // Update the creation-time of the newly created cookie to match the creation-time of the old-cookie. - cookie.creation_time = old_cookie.creation_time; + // 11. If the cookie store contains a cookie with the same name, domain, and path as the newly created cookie: + if (auto old_cookie = m_transient_storage.get_cookie(key); old_cookie.has_value()) { + // Update the creation-time of the newly created cookie to match the creation-time of the old-cookie. + cookie.creation_time = old_cookie->creation_time; - // Remove the old-cookie from the cookie store. - // NOTE: Rather than deleting then re-inserting this cookie, we update it in-place. - update_cookie_in_database(cookie); - }, + // Remove the old-cookie from the cookie store. + // NOTE: Rather than deleting then re-inserting this cookie, we update it in-place. + } - // 12. Insert the newly created cookie into the cookie store. - [this](auto cookie) { - insert_cookie_into_database(cookie); - }); + // 12. Insert the newly created cookie into the cookie store. + m_transient_storage.set_cookie(move(key), move(cookie)); + + m_transient_storage.purge_expired_cookies(); } void CookieJar::dump_cookies() { - static constexpr auto key_color = "\033[34;1m"sv; - static constexpr auto attribute_color = "\033[33m"sv; - static constexpr auto no_color = "\033[0m"sv; - StringBuilder builder; - size_t total_cookies { 0 }; - select_all_cookies_from_database([&](auto cookie) { - ++total_cookies; + m_transient_storage.for_each_cookie([&](auto const& cookie) { + static constexpr auto key_color = "\033[34;1m"sv; + static constexpr auto attribute_color = "\033[33m"sv; + static constexpr auto no_color = "\033[0m"sv; builder.appendff("{}{}{} - ", key_color, cookie.name, no_color); builder.appendff("{}{}{} - ", key_color, cookie.domain, no_color); @@ -166,15 +181,16 @@ void CookieJar::dump_cookies() builder.appendff("\t{}SameSite{} = {:s}\n", attribute_color, no_color, Web::Cookie::same_site_to_string(cookie.same_site)); }); - dbgln("{} cookies stored\n{}", total_cookies, builder.string_view()); + dbgln("{} cookies stored\n{}", m_transient_storage.size(), builder.string_view()); } Vector CookieJar::get_all_cookies() { Vector cookies; + cookies.ensure_capacity(m_transient_storage.size()); - select_all_cookies_from_database([&](auto cookie) { - cookies.append(move(cookie)); + m_transient_storage.for_each_cookie([&](auto const& cookie) { + cookies.unchecked_append(cookie); }); return cookies; @@ -310,7 +326,7 @@ void CookieJar::store_cookie(Web::Cookie::ParsedCookie const& parsed_cookie, con } else { // Set the cookie's persistent-flag to false. Set the cookie's expiry-time to the latest representable date. cookie.persistent = false; - cookie.expiry_time = UnixDateTime::latest(); + cookie.expiry_time = UnixDateTime::from_unix_time_parts(3000, 1, 1, 0, 0, 0, 0); } // 4. If the cookie-attribute-list contains an attribute with an attribute-name of "Domain": @@ -365,46 +381,37 @@ void CookieJar::store_cookie(Web::Cookie::ParsedCookie const& parsed_cookie, con if (source != Web::Cookie::Source::Http && cookie.http_only) return; - // Synchronize persisting the cookie - auto sync_promise = Core::Promise::construct(); + CookieStorageKey key { cookie.name, cookie.domain, cookie.path }; - select_cookie_from_database( - move(cookie), + // 11. If the cookie store contains a cookie with the same name, domain, and path as the newly created cookie: + if (auto const& old_cookie = m_transient_storage.get_cookie(key); old_cookie.has_value()) { + // If the newly created cookie was received from a "non-HTTP" API and the old-cookie's http-only-flag is set, abort these + // steps and ignore the newly created cookie entirely. + if (source != Web::Cookie::Source::Http && old_cookie->http_only) + return; - // 11. If the cookie store contains a cookie with the same name, domain, and path as the newly created cookie: - [this, source, sync_promise](auto& cookie, auto old_cookie) { - ScopeGuard guard { [&]() { sync_promise->resolve({}); } }; + // Update the creation-time of the newly created cookie to match the creation-time of the old-cookie. + cookie.creation_time = old_cookie->creation_time; - // If the newly created cookie was received from a "non-HTTP" API and the old-cookie's http-only-flag is set, abort these - // steps and ignore the newly created cookie entirely. - if (source != Web::Cookie::Source::Http && old_cookie.http_only) - return; + // Remove the old-cookie from the cookie store. + // NOTE: Rather than deleting then re-inserting this cookie, we update it in-place. + } - // Update the creation-time of the newly created cookie to match the creation-time of the old-cookie. - cookie.creation_time = old_cookie.creation_time; + // 12. Insert the newly created cookie into the cookie store. + m_transient_storage.set_cookie(move(key), move(cookie)); - // Remove the old-cookie from the cookie store. - // NOTE: Rather than deleting then re-inserting this cookie, we update it in-place. - update_cookie_in_database(cookie); - }, - - // 12. Insert the newly created cookie into the cookie store. - [this, sync_promise](auto cookie) { - insert_cookie_into_database(cookie); - sync_promise->resolve({}); - }); - - MUST(sync_promise->await()); + m_transient_storage.purge_expired_cookies(); } Vector CookieJar::get_matching_cookies(const URL::URL& url, StringView canonicalized_domain, Web::Cookie::Source source, MatchingCookiesSpecMode mode) { // https://tools.ietf.org/html/rfc6265#section-5.4 + auto now = UnixDateTime::now(); // 1. Let cookie-list be the set of cookies from the cookie store that meets all of the following requirements: Vector cookie_list; - select_all_cookies_from_database([&](auto cookie) { + m_transient_storage.for_each_cookie([&](auto& cookie) { // Either: The cookie's host-only-flag is true and the canonicalized request-host is identical to the cookie's domain. // Or: The cookie's host-only-flag is false and the canonicalized request-host domain-matches the cookie's domain. bool is_host_only_and_has_identical_domain = cookie.host_only && (canonicalized_domain == cookie.domain); @@ -426,20 +433,25 @@ Vector CookieJar::get_matching_cookies(const URL::URL& url, // NOTE: The WebDriver spec expects only step 1 above to be executed to match cookies. if (mode == MatchingCookiesSpecMode::WebDriver) { - cookie_list.append(move(cookie)); + cookie_list.append(cookie); return; } + // 3. Update the last-access-time of each cookie in the cookie-list to the current date and time. + // NOTE: We do this first so that both our internal storage and cookie-list are updated. + cookie.last_access_time = now; + // 2. The user agent SHOULD sort the cookie-list in the following order: // - Cookies with longer paths are listed before cookies with shorter paths. // - Among cookies that have equal-length path fields, cookies with earlier creation-times are listed before cookies with later creation-times. auto cookie_path_length = cookie.path.bytes().size(); auto cookie_creation_time = cookie.creation_time; - cookie_list.insert_before_matching(move(cookie), [cookie_path_length, cookie_creation_time](auto const& entry) { + cookie_list.insert_before_matching(cookie, [cookie_path_length, cookie_creation_time](auto const& entry) { if (cookie_path_length > entry.path.bytes().size()) { return true; - } else if (cookie_path_length == entry.path.bytes().size()) { + } + if (cookie_path_length == entry.path.bytes().size()) { if (cookie_creation_time < entry.creation_time) return true; } @@ -447,19 +459,9 @@ Vector CookieJar::get_matching_cookies(const URL::URL& url, }); }); - // NOTE: The WebDriver spec expects only step 1 above to be executed to match cookies. - if (mode == MatchingCookiesSpecMode::WebDriver) - return cookie_list; + if (mode != MatchingCookiesSpecMode::WebDriver) + m_transient_storage.purge_expired_cookies(); - // 3. Update the last-access-time of each cookie in the cookie-list to the current date and time. - auto now = UnixDateTime::now(); - - for (auto& cookie : cookie_list) { - cookie.last_access_time = now; - update_cookie_last_access_time_in_database(cookie); - } - - purge_expired_cookies(); return cookie_list; } @@ -527,161 +529,110 @@ static ErrorOr parse_cookie(ReadonlySpan row) return cookie; } -void CookieJar::insert_cookie_into_database(Web::Cookie::Cookie const& cookie) +void CookieJar::TransientStorage::set_cookies(Cookies cookies) { - m_storage.visit( - [&](PersistedStorage& storage) { - storage.database.execute_statement( - storage.statements.insert_cookie, {}, [this]() { purge_expired_cookies(); }, {}, - cookie.name, - cookie.value, - to_underlying(cookie.same_site), - cookie.creation_time, - cookie.last_access_time, - cookie.expiry_time, - cookie.domain, - cookie.path, - cookie.secure, - cookie.http_only, - cookie.host_only, - cookie.persistent); - }, - [&](TransientStorage& storage) { - CookieStorageKey key { cookie.name, cookie.domain, cookie.path }; - storage.set(key, cookie); - }); + m_cookies = move(cookies); + purge_expired_cookies(); } -void CookieJar::update_cookie_in_database(Web::Cookie::Cookie const& cookie) +void CookieJar::TransientStorage::set_cookie(CookieStorageKey key, Web::Cookie::Cookie cookie) { - m_storage.visit( - [&](PersistedStorage& storage) { - storage.database.execute_statement( - storage.statements.update_cookie, {}, [this]() { purge_expired_cookies(); }, {}, - cookie.value, - to_underlying(cookie.same_site), - cookie.creation_time, - cookie.last_access_time, - cookie.expiry_time, - cookie.secure, - cookie.http_only, - cookie.host_only, - cookie.persistent, - cookie.name, - cookie.domain, - cookie.path); - }, - [&](TransientStorage& storage) { - CookieStorageKey key { cookie.name, cookie.domain, cookie.path }; - storage.set(key, cookie); - }); -} + auto result = m_cookies.set(key, cookie); -void CookieJar::update_cookie_last_access_time_in_database(Web::Cookie::Cookie const& cookie) -{ - m_storage.visit( - [&](PersistedStorage& storage) { - storage.database.execute_statement( - storage.statements.update_cookie_last_access_time, - {}, {}, {}, - cookie.last_access_time, - cookie.name, - cookie.domain, - cookie.path); - }, - [&](TransientStorage& storage) { - CookieStorageKey key { cookie.name, cookie.domain, cookie.path }; - storage.set(key, cookie); - }); -} + switch (result) { + case HashSetResult::InsertedNewEntry: + m_inserted_cookies.set(move(key), move(cookie)); + break; -struct WrappedCookie : public RefCounted { - explicit WrappedCookie(Web::Cookie::Cookie cookie_) - : RefCounted() - , cookie(move(cookie_)) - { + case HashSetResult::ReplacedExistingEntry: + if (m_inserted_cookies.contains(key)) + m_inserted_cookies.set(move(key), move(cookie)); + else + m_updated_cookies.set(move(key), move(cookie)); + break; + + case HashSetResult::KeptExistingEntry: + VERIFY_NOT_REACHED(); + break; } - - Web::Cookie::Cookie cookie; - bool had_any_results { false }; -}; - -void CookieJar::select_cookie_from_database(Web::Cookie::Cookie cookie, OnCookieFound on_result, OnCookieNotFound on_complete_without_results) -{ - m_storage.visit( - [&](PersistedStorage& storage) { - auto wrapped_cookie = make_ref_counted(move(cookie)); - - storage.database.execute_statement( - storage.statements.select_cookie, - [on_result = move(on_result), wrapped_cookie = wrapped_cookie](auto row) { - if (auto selected_cookie = parse_cookie(row); selected_cookie.is_error()) - dbgln("Failed to parse cookie '{}': {}", selected_cookie.error(), row); - else - on_result(wrapped_cookie->cookie, selected_cookie.release_value()); - - wrapped_cookie->had_any_results = true; - }, - [on_complete_without_results = move(on_complete_without_results), wrapped_cookie = wrapped_cookie]() { - if (!wrapped_cookie->had_any_results) - on_complete_without_results(move(wrapped_cookie->cookie)); - }, - {}, - wrapped_cookie->cookie.name, - wrapped_cookie->cookie.domain, - wrapped_cookie->cookie.path); - }, - [&](TransientStorage& storage) { - CookieStorageKey key { cookie.name, cookie.domain, cookie.path }; - - if (auto it = storage.find(key); it != storage.end()) - on_result(cookie, it->value); - else - on_complete_without_results(cookie); - }); } -void CookieJar::select_all_cookies_from_database(OnSelectAllCookiesResult on_result) +Optional CookieJar::TransientStorage::get_cookie(CookieStorageKey const& key) { - // FIXME: Make surrounding APIs asynchronous. - m_storage.visit( - [&](PersistedStorage& storage) { - storage.database.execute_statement( - storage.statements.select_all_cookies, - [on_result = move(on_result)](auto row) { - if (auto cookie = parse_cookie(row); cookie.is_error()) - dbgln("Failed to parse cookie '{}': {}", cookie.error(), row); - else - on_result(cookie.release_value()); - }, - {}, - {}); - }, - [&](TransientStorage& storage) { - for (auto const& cookie : storage) - on_result(cookie.value); - }); + return m_cookies.get(key); } -void CookieJar::purge_expired_cookies() +UnixDateTime CookieJar::TransientStorage::purge_expired_cookies() { auto now = UnixDateTime::now(); + auto is_expired = [&](auto const&, auto const& cookie) { return cookie.expiry_time < now; }; - m_storage.visit( - [&](PersistedStorage& storage) { - storage.database.execute_statement(storage.statements.expire_cookie, {}, {}, {}, now); + m_cookies.remove_all_matching(is_expired); + m_inserted_cookies.remove_all_matching(is_expired); + m_updated_cookies.remove_all_matching(is_expired); + + return now; +} + +void CookieJar::PersistedStorage::insert_cookie(Web::Cookie::Cookie const& cookie) +{ + database.execute_statement( + statements.insert_cookie, + {}, {}, {}, + cookie.name, + cookie.value, + to_underlying(cookie.same_site), + cookie.creation_time, + cookie.last_access_time, + cookie.expiry_time, + cookie.domain, + cookie.path, + cookie.secure, + cookie.http_only, + cookie.host_only, + cookie.persistent); +} + +void CookieJar::PersistedStorage::update_cookie(Web::Cookie::Cookie const& cookie) +{ + database.execute_statement( + statements.update_cookie, + {}, {}, {}, + cookie.value, + to_underlying(cookie.same_site), + cookie.creation_time, + cookie.last_access_time, + cookie.expiry_time, + cookie.secure, + cookie.http_only, + cookie.host_only, + cookie.persistent, + cookie.name, + cookie.domain, + cookie.path); +} + +CookieJar::TransientStorage::Cookies CookieJar::PersistedStorage::select_all_cookies() +{ + HashMap cookies; + + auto add_cookie = [&](auto cookie) { + CookieStorageKey key { cookie.name, cookie.domain, cookie.path }; + cookies.set(move(key), move(cookie)); + }; + + database.execute_statement( + statements.select_all_cookies, + [&](auto row) { + if (auto cookie = parse_cookie(row); cookie.is_error()) + dbgln("Failed to parse cookie '{}': {}", cookie.error(), row); + else + add_cookie(cookie.release_value()); }, - [&](TransientStorage& storage) { - Vector keys_to_evict; + {}, + {}); - for (auto const& cookie : storage) { - if (cookie.value.expiry_time < now) - keys_to_evict.append(cookie.key); - } - - for (auto const& key : keys_to_evict) - storage.remove(key); - }); + return cookies; } } diff --git a/Userland/Libraries/LibWebView/CookieJar.h b/Userland/Libraries/LibWebView/CookieJar.h index 3762382b90..9f38de9977 100644 --- a/Userland/Libraries/LibWebView/CookieJar.h +++ b/Userland/Libraries/LibWebView/CookieJar.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -34,22 +35,53 @@ class CookieJar { SQL::StatementID create_table { 0 }; SQL::StatementID insert_cookie { 0 }; SQL::StatementID update_cookie { 0 }; - SQL::StatementID update_cookie_last_access_time { 0 }; SQL::StatementID expire_cookie { 0 }; - SQL::StatementID select_cookie { 0 }; SQL::StatementID select_all_cookies { 0 }; }; - struct PersistedStorage { - Database& database; - Statements statements; + class TransientStorage { + public: + using Cookies = HashMap; + + void set_cookies(Cookies); + void set_cookie(CookieStorageKey, Web::Cookie::Cookie); + Optional get_cookie(CookieStorageKey const&); + + size_t size() const { return m_cookies.size(); } + + UnixDateTime purge_expired_cookies(); + + auto take_inserted_cookies() { return move(m_inserted_cookies); } + auto take_updated_cookies() { return move(m_updated_cookies); } + + template + void for_each_cookie(Callback callback) + { + for (auto& it : m_cookies) + callback(it.value); + } + + private: + Cookies m_cookies; + Cookies m_inserted_cookies; + Cookies m_updated_cookies; }; - using TransientStorage = HashMap; + struct PersistedStorage { + void insert_cookie(Web::Cookie::Cookie const& cookie); + void update_cookie(Web::Cookie::Cookie const& cookie); + TransientStorage::Cookies select_all_cookies(); + + Database& database; + Statements statements; + RefPtr synchronization_timer {}; + }; public: - static ErrorOr create(Database&); - static CookieJar create(); + static ErrorOr> create(Database&); + static NonnullOwnPtr create(); + + ~CookieJar(); String get_cookie(const URL::URL& url, Web::Cookie::Source source); void set_cookie(const URL::URL& url, Web::Cookie::ParsedCookie const& parsed_cookie, Web::Cookie::Source source); @@ -60,8 +92,10 @@ public: Optional get_named_cookie(URL::URL const& url, StringView name); private: - explicit CookieJar(PersistedStorage); - explicit CookieJar(TransientStorage); + explicit CookieJar(Optional); + + AK_MAKE_NONCOPYABLE(CookieJar); + AK_MAKE_NONMOVABLE(CookieJar); static Optional canonicalize_domain(const URL::URL& url); static bool domain_matches(StringView string, StringView domain_string); @@ -76,20 +110,8 @@ private: void store_cookie(Web::Cookie::ParsedCookie const& parsed_cookie, const URL::URL& url, String canonicalized_domain, Web::Cookie::Source source); Vector get_matching_cookies(const URL::URL& url, StringView canonicalized_domain, Web::Cookie::Source source, MatchingCookiesSpecMode mode = MatchingCookiesSpecMode::RFC6265); - void insert_cookie_into_database(Web::Cookie::Cookie const& cookie); - void update_cookie_in_database(Web::Cookie::Cookie const& cookie); - void update_cookie_last_access_time_in_database(Web::Cookie::Cookie const& cookie); - - using OnCookieFound = Function; - using OnCookieNotFound = Function; - void select_cookie_from_database(Web::Cookie::Cookie cookie, OnCookieFound on_result, OnCookieNotFound on_complete_without_results); - - using OnSelectAllCookiesResult = Function; - void select_all_cookies_from_database(OnSelectAllCookiesResult on_result); - - void purge_expired_cookies(); - - Variant m_storage; + Optional m_persisted_storage; + TransientStorage m_transient_storage; }; } diff --git a/Userland/Utilities/headless-browser.cpp b/Userland/Utilities/headless-browser.cpp index 53001f2e4f..bc5a8756b2 100644 --- a/Userland/Utilities/headless-browser.cpp +++ b/Userland/Utilities/headless-browser.cpp @@ -164,7 +164,7 @@ public: } private: - HeadlessWebContentView(NonnullRefPtr database, WebView::CookieJar cookie_jar, RefPtr request_client = nullptr) + HeadlessWebContentView(NonnullRefPtr database, NonnullOwnPtr cookie_jar, RefPtr request_client = nullptr) : m_database(move(database)) , m_cookie_jar(move(cookie_jar)) , m_request_client(move(request_client)) @@ -183,11 +183,11 @@ private: }; on_get_cookie = [this](auto const& url, auto source) { - return m_cookie_jar.get_cookie(url, source); + return m_cookie_jar->get_cookie(url, source); }; on_set_cookie = [this](auto const& url, auto const& cookie, auto source) { - m_cookie_jar.set_cookie(url, cookie, source); + m_cookie_jar->set_cookie(url, cookie, source); }; on_request_worker_agent = [this]() { @@ -213,7 +213,7 @@ private: RefPtr>> m_pending_screenshot; NonnullRefPtr m_database; - WebView::CookieJar m_cookie_jar; + NonnullOwnPtr m_cookie_jar; RefPtr m_request_client; };