2024-05-19 17:58:06 +12:00
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
|
2024-11-04 18:19:19 +01:00
|
|
|
|
* Copyright (c) 2024, stelar7 <dudedbz@gmail.com>
|
2024-05-19 17:58:06 +12:00
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <LibWeb/Bindings/IDBFactoryPrototype.h>
|
|
|
|
|
#include <LibWeb/Bindings/Intrinsics.h>
|
2024-11-04 18:19:19 +01:00
|
|
|
|
#include <LibWeb/DOM/Event.h>
|
|
|
|
|
#include <LibWeb/HTML/EventNames.h>
|
|
|
|
|
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
2024-11-04 19:10:07 +01:00
|
|
|
|
#include <LibWeb/IndexedDB/IDBDatabase.h>
|
2024-05-19 17:58:06 +12:00
|
|
|
|
#include <LibWeb/IndexedDB/IDBFactory.h>
|
2024-11-04 19:10:07 +01:00
|
|
|
|
#include <LibWeb/IndexedDB/Internal/Algorithms.h>
|
2024-11-11 10:49:44 +01:00
|
|
|
|
#include <LibWeb/IndexedDB/Internal/Key.h>
|
2024-11-04 18:19:19 +01:00
|
|
|
|
#include <LibWeb/Platform/EventLoopPlugin.h>
|
|
|
|
|
#include <LibWeb/StorageAPI/StorageKey.h>
|
2024-05-19 17:58:06 +12:00
|
|
|
|
|
|
|
|
|
namespace Web::IndexedDB {
|
|
|
|
|
|
2024-11-15 04:01:23 +13:00
|
|
|
|
GC_DEFINE_ALLOCATOR(IDBFactory);
|
2024-05-19 17:58:06 +12:00
|
|
|
|
|
|
|
|
|
IDBFactory::IDBFactory(JS::Realm& realm)
|
|
|
|
|
: Bindings::PlatformObject(realm)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IDBFactory::~IDBFactory() = default;
|
|
|
|
|
|
|
|
|
|
void IDBFactory::initialize(JS::Realm& realm)
|
|
|
|
|
{
|
|
|
|
|
Base::initialize(realm);
|
|
|
|
|
WEB_SET_PROTOTYPE_FOR_INTERFACE(IDBFactory);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-04 18:19:19 +01:00
|
|
|
|
// https://w3c.github.io/IndexedDB/#dom-idbfactory-open
|
2024-11-15 04:01:23 +13:00
|
|
|
|
WebIDL::ExceptionOr<GC::Ref<IDBOpenDBRequest>> IDBFactory::open(String const& name, Optional<u64> version)
|
2024-11-04 18:19:19 +01:00
|
|
|
|
{
|
|
|
|
|
auto& realm = this->realm();
|
|
|
|
|
|
|
|
|
|
// 1. If version is 0 (zero), throw a TypeError.
|
|
|
|
|
if (version.has_value() && version.value() == 0)
|
|
|
|
|
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "The version provided must not be 0"_string };
|
|
|
|
|
|
|
|
|
|
// 2. Let environment be this's relevant settings object.
|
|
|
|
|
auto& environment = HTML::relevant_settings_object(*this);
|
|
|
|
|
|
|
|
|
|
// 3. Let storageKey be the result of running obtain a storage key given environment.
|
|
|
|
|
// If failure is returned, then throw a "SecurityError" DOMException and abort these steps.
|
|
|
|
|
auto storage_key = StorageAPI::obtain_a_storage_key(environment);
|
|
|
|
|
if (!storage_key.has_value())
|
|
|
|
|
return WebIDL::SecurityError::create(realm, "Failed to obtain a storage key"_string);
|
|
|
|
|
|
|
|
|
|
// 4. Let request be a new open request.
|
|
|
|
|
auto request = IDBOpenDBRequest::create(realm);
|
|
|
|
|
|
|
|
|
|
// 5. Run these steps in parallel:
|
2024-11-15 04:01:23 +13:00
|
|
|
|
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(realm.heap(), [&realm, storage_key, name, version, request] {
|
2024-11-04 18:19:19 +01:00
|
|
|
|
HTML::TemporaryExecutionContext context(realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
|
|
|
|
|
|
2024-11-04 19:10:07 +01:00
|
|
|
|
// 1. Let result be the result of opening a database connection, with storageKey, name, version if given and undefined otherwise, and request.
|
|
|
|
|
auto result = open_a_database_connection(realm, storage_key.value(), name, version, request);
|
|
|
|
|
|
2024-12-13 23:58:11 +01:00
|
|
|
|
// FIXME: https://github.com/w3c/IndexedDB/issues/434
|
|
|
|
|
// We need to set the request as processed, since we didnt always do it via the above function.
|
|
|
|
|
request->set_processed(true);
|
|
|
|
|
|
2024-11-04 19:10:07 +01:00
|
|
|
|
// 2. Queue a task to run these steps:
|
2024-11-07 20:43:25 +01:00
|
|
|
|
HTML::queue_a_task(HTML::Task::Source::DatabaseAccess, nullptr, nullptr, GC::create_function(realm.heap(), [&realm, request, result = move(result)]() mutable {
|
2024-11-04 19:10:07 +01:00
|
|
|
|
// 1. If result is an error, then:
|
|
|
|
|
if (result.is_error()) {
|
|
|
|
|
// 1. Set request’s result to undefined.
|
|
|
|
|
request->set_result(JS::js_undefined());
|
|
|
|
|
|
|
|
|
|
// 2. Set request’s error to result.
|
2024-11-15 04:01:23 +13:00
|
|
|
|
request->set_error(result.exception().get<GC::Ref<WebIDL::DOMException>>());
|
2024-11-04 19:10:07 +01:00
|
|
|
|
|
|
|
|
|
// 3. Set request’s done flag to true.
|
|
|
|
|
request->set_done(true);
|
|
|
|
|
|
|
|
|
|
// 4. Fire an event named error at request with its bubbles and cancelable attributes initialized to true.
|
|
|
|
|
request->dispatch_event(DOM::Event::create(realm, HTML::EventNames::error));
|
|
|
|
|
} else {
|
|
|
|
|
// 1. Set request’s result to result.
|
|
|
|
|
request->set_result(result.release_value());
|
|
|
|
|
|
|
|
|
|
// 2. Set request’s done flag to true.
|
|
|
|
|
request->set_done(true);
|
|
|
|
|
|
|
|
|
|
// 3. Fire an event named success at request.
|
|
|
|
|
request->dispatch_event(DOM::Event::create(realm, HTML::EventNames::success));
|
|
|
|
|
}
|
|
|
|
|
}));
|
2024-11-04 18:19:19 +01:00
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// 6. Return a new IDBOpenDBRequest object for request.
|
|
|
|
|
return request;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-11 10:49:44 +01:00
|
|
|
|
// https://w3c.github.io/IndexedDB/#dom-idbfactory-cmp
|
|
|
|
|
WebIDL::ExceptionOr<i8> IDBFactory::cmp(JS::Value first, JS::Value second)
|
|
|
|
|
{
|
|
|
|
|
// 1. Let a be the result of converting a value to a key with first. Rethrow any exceptions.
|
|
|
|
|
auto a = convert_a_value_to_a_key(realm(), first);
|
|
|
|
|
|
|
|
|
|
// 2. If a is invalid, throw a "DataError" DOMException.
|
|
|
|
|
if (a.is_error())
|
|
|
|
|
return WebIDL::DataError::create(realm(), "Failed to convert a value to a key"_string);
|
|
|
|
|
|
|
|
|
|
// 3. Let b be the result of converting a value to a key with second. Rethrow any exceptions.
|
|
|
|
|
auto b = convert_a_value_to_a_key(realm(), second);
|
|
|
|
|
|
|
|
|
|
// 4. If b is invalid, throw a "DataError" DOMException.
|
|
|
|
|
if (b.is_error())
|
|
|
|
|
return WebIDL::DataError::create(realm(), "Failed to convert a value to a key"_string);
|
|
|
|
|
|
|
|
|
|
// 5. Return the results of comparing two keys with a and b.
|
|
|
|
|
return Key::compare_two_keys(a.release_value(), b.release_value());
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-01 21:12:35 +01:00
|
|
|
|
// https://w3c.github.io/IndexedDB/#dom-idbfactory-deletedatabase
|
|
|
|
|
WebIDL::ExceptionOr<GC::Ref<IDBOpenDBRequest>> IDBFactory::delete_database(String const& name)
|
|
|
|
|
{
|
|
|
|
|
auto& realm = this->realm();
|
|
|
|
|
|
|
|
|
|
// 1. Let environment be this's relevant settings object.
|
|
|
|
|
auto& environment = HTML::relevant_settings_object(*this);
|
|
|
|
|
|
|
|
|
|
// 2. Let storageKey be the result of running obtain a storage key given environment.
|
|
|
|
|
// If failure is returned, then throw a "SecurityError" DOMException and abort these steps.
|
|
|
|
|
auto storage_key = StorageAPI::obtain_a_storage_key(environment);
|
|
|
|
|
if (!storage_key.has_value())
|
|
|
|
|
return WebIDL::SecurityError::create(realm, "Failed to obtain a storage key"_string);
|
|
|
|
|
|
|
|
|
|
// 3. Let request be a new open request.
|
|
|
|
|
auto request = IDBOpenDBRequest::create(realm);
|
|
|
|
|
|
|
|
|
|
// 4. Run these steps in parallel:
|
|
|
|
|
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(realm.heap(), [&realm, storage_key, name, request] {
|
|
|
|
|
HTML::TemporaryExecutionContext context(realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
|
|
|
|
|
|
|
|
|
|
// 1. Let result be the result of deleting a database, with storageKey, name, and request.
|
|
|
|
|
auto result = delete_a_database(realm, storage_key.value(), name, request);
|
|
|
|
|
|
|
|
|
|
// 2. Set request’s processed flag to true.
|
|
|
|
|
request->set_processed(true);
|
|
|
|
|
|
|
|
|
|
// 3. Queue a task to run these steps:
|
|
|
|
|
HTML::queue_a_task(HTML::Task::Source::DatabaseAccess, nullptr, nullptr, GC::create_function(realm.heap(), [&realm, request, result = move(result)]() mutable {
|
|
|
|
|
// 1. If result is an error,
|
|
|
|
|
if (result.is_error()) {
|
|
|
|
|
// set request’s error to result,
|
|
|
|
|
request->set_error(result.exception().get<GC::Ref<WebIDL::DOMException>>());
|
|
|
|
|
// set request’s done flag to true,
|
|
|
|
|
request->set_done(true);
|
|
|
|
|
// and fire an event named error at request with its bubbles and cancelable attributes initialized to true.
|
|
|
|
|
request->dispatch_event(DOM::Event::create(realm, HTML::EventNames::error, { .bubbles = true, .cancelable = true }));
|
|
|
|
|
}
|
|
|
|
|
// 2. Otherwise,
|
|
|
|
|
else {
|
|
|
|
|
// set request’s result to undefined,
|
|
|
|
|
request->set_result(JS::js_undefined());
|
|
|
|
|
// set request’s done flag to true,
|
|
|
|
|
request->set_done(true);
|
|
|
|
|
// and fire a version change event named success at request with result and null.
|
|
|
|
|
auto value = result.release_value();
|
|
|
|
|
fire_a_version_change_event(realm, HTML::EventNames::success, request, value, {});
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// 5. Return a new IDBOpenDBRequest object for request.
|
|
|
|
|
return request;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-19 17:58:06 +12:00
|
|
|
|
}
|