AK+LibWeb: Move generation of random UUIDs into AK

This will let us use this more outside of LibWeb more easily.

Stop handling tiny OOM while we are here.
This commit is contained in:
Timothy Flynn 2026-03-24 08:33:21 -04:00 committed by Tim Flynn
parent 16dffe39a0
commit 58791db818
14 changed files with 74 additions and 75 deletions

View file

@ -8,6 +8,7 @@
#include <AK/Platform.h> #include <AK/Platform.h>
#include <AK/Random.h> #include <AK/Random.h>
#include <AK/String.h>
#include <AK/UFixedBigInt.h> #include <AK/UFixedBigInt.h>
#include <AK/UFixedBigIntDivision.h> #include <AK/UFixedBigIntDivision.h>
@ -89,6 +90,52 @@ u64 get_random_uniform_64(u64 max_bounds)
return random_value % max_bounds; return random_value % max_bounds;
} }
// https://w3c.github.io/webcrypto/#dfn-generate-a-random-uuid
String generate_random_uuid()
{
// 1. Let bytes be a byte sequence of length 16.
u8 bytes[16];
// 2. Fill bytes with cryptographically secure random bytes.
fill_with_random(bytes);
// 3. Set the 4 most significant bits of bytes[6], which represent the UUID version, to 0100.
bytes[6] = (bytes[6] & 0x0f) | 0x40;
// 4. Set the 2 most significant bits of bytes[8], which represent the UUID variant, to 10.
bytes[8] = (bytes[8] & 0x3f) | 0x80;
// 5. Return the string concatenation of «
// hexadecimal representation of bytes[0],
// hexadecimal representation of bytes[1],
// hexadecimal representation of bytes[2],
// hexadecimal representation of bytes[3],
// "-",
// hexadecimal representation of bytes[4],
// hexadecimal representation of bytes[5],
// "-",
// hexadecimal representation of bytes[6],
// hexadecimal representation of bytes[7],
// "-",
// hexadecimal representation of bytes[8],
// hexadecimal representation of bytes[9],
// "-",
// hexadecimal representation of bytes[10],
// hexadecimal representation of bytes[11],
// hexadecimal representation of bytes[12],
// hexadecimal representation of bytes[13],
// hexadecimal representation of bytes[14],
// hexadecimal representation of bytes[15]
// ».
return MUST(String::formatted(
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
bytes[0], bytes[1], bytes[2], bytes[3],
bytes[4], bytes[5],
bytes[6], bytes[7],
bytes[8], bytes[9],
bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]));
}
XorShift128PlusRNG::XorShift128PlusRNG() XorShift128PlusRNG::XorShift128PlusRNG()
{ {
// Splitmix64 is used as xorshift is sensitive to being seeded with all 0s // Splitmix64 is used as xorshift is sensitive to being seeded with all 0s

View file

@ -31,6 +31,8 @@ inline T get_random()
u32 get_random_uniform(u32 max_bounds); u32 get_random_uniform(u32 max_bounds);
u64 get_random_uniform_64(u64 max_bounds); u64 get_random_uniform_64(u64 max_bounds);
String generate_random_uuid();
// http://vigna.di.unimi.it/ftp/papers/xorshiftplus.pdf // http://vigna.di.unimi.it/ftp/papers/xorshiftplus.pdf
class XorShift128PlusRNG { class XorShift128PlusRNG {
public: public:
@ -58,6 +60,7 @@ inline void shuffle(Collection& collection)
#if USING_AK_GLOBALLY #if USING_AK_GLOBALLY
using AK::fill_with_random; using AK::fill_with_random;
using AK::generate_random_uuid;
using AK::get_random; using AK::get_random;
using AK::get_random_uniform; using AK::get_random_uniform;
using AK::shuffle; using AK::shuffle;

View file

@ -6,10 +6,8 @@
*/ */
#include <AK/Random.h> #include <AK/Random.h>
#include <AK/StringBuilder.h>
#include <LibJS/Runtime/TypedArray.h> #include <LibJS/Runtime/TypedArray.h>
#include <LibWeb/Bindings/CryptoPrototype.h> #include <LibWeb/Bindings/CryptoPrototype.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Crypto/Crypto.h> #include <LibWeb/Crypto/Crypto.h>
#include <LibWeb/Crypto/SubtleCrypto.h> #include <LibWeb/Crypto/SubtleCrypto.h>
@ -39,6 +37,12 @@ void Crypto::initialize(JS::Realm& realm)
m_subtle = SubtleCrypto::create(realm); m_subtle = SubtleCrypto::create(realm);
} }
void Crypto::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_subtle);
}
GC::Ref<SubtleCrypto> Crypto::subtle() const GC::Ref<SubtleCrypto> Crypto::subtle() const
{ {
return *m_subtle; return *m_subtle;
@ -73,70 +77,15 @@ WebIDL::ExceptionOr<GC::Root<WebIDL::ArrayBufferView>> Crypto::get_random_values
} }
// https://w3c.github.io/webcrypto/#dfn-Crypto-method-randomUUID // https://w3c.github.io/webcrypto/#dfn-Crypto-method-randomUUID
WebIDL::ExceptionOr<String> Crypto::random_uuid() const String Crypto::random_uuid() const
{ {
auto& vm = realm().vm(); return generate_random_uuid();
return TRY_OR_THROW_OOM(vm, generate_random_uuid());
}
void Crypto::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_subtle);
} }
// https://w3c.github.io/webcrypto/#dfn-generate-a-random-uuid // https://w3c.github.io/webcrypto/#dfn-generate-a-random-uuid
ErrorOr<String> generate_random_uuid() String generate_random_uuid()
{ {
// 1. Let bytes be a byte sequence of length 16. return AK::generate_random_uuid();
u8 bytes[16];
// 2. Fill bytes with cryptographically secure random bytes.
fill_with_random(bytes);
// 3. Set the 4 most significant bits of bytes[6], which represent the UUID version, to 0100.
bytes[6] &= ~(1 << 7);
bytes[6] |= 1 << 6;
bytes[6] &= ~(1 << 5);
bytes[6] &= ~(1 << 4);
// 4. Set the 2 most significant bits of bytes[8], which represent the UUID variant, to 10.
bytes[8] |= 1 << 7;
bytes[8] &= ~(1 << 6);
/* 5. Return the string concatenation of
«
hexadecimal representation of bytes[0],
hexadecimal representation of bytes[1],
hexadecimal representation of bytes[2],
hexadecimal representation of bytes[3],
"-",
hexadecimal representation of bytes[4],
hexadecimal representation of bytes[5],
"-",
hexadecimal representation of bytes[6],
hexadecimal representation of bytes[7],
"-",
hexadecimal representation of bytes[8],
hexadecimal representation of bytes[9],
"-",
hexadecimal representation of bytes[10],
hexadecimal representation of bytes[11],
hexadecimal representation of bytes[12],
hexadecimal representation of bytes[13],
hexadecimal representation of bytes[14],
hexadecimal representation of bytes[15]
».
*/
StringBuilder builder;
TRY(builder.try_appendff("{:02x}{:02x}{:02x}{:02x}-", bytes[0], bytes[1], bytes[2], bytes[3]));
TRY(builder.try_appendff("{:02x}{:02x}-", bytes[4], bytes[5]));
TRY(builder.try_appendff("{:02x}{:02x}-", bytes[6], bytes[7]));
TRY(builder.try_appendff("{:02x}{:02x}-", bytes[8], bytes[9]));
TRY(builder.try_appendff("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]));
return builder.to_string();
} }
} }

View file

@ -25,7 +25,7 @@ public:
GC::Ref<SubtleCrypto> subtle() const; GC::Ref<SubtleCrypto> subtle() const;
WebIDL::ExceptionOr<GC::Root<WebIDL::ArrayBufferView>> get_random_values(GC::Root<WebIDL::ArrayBufferView>) const; WebIDL::ExceptionOr<GC::Root<WebIDL::ArrayBufferView>> get_random_values(GC::Root<WebIDL::ArrayBufferView>) const;
WebIDL::ExceptionOr<String> random_uuid() const; String random_uuid() const;
protected: protected:
virtual void initialize(JS::Realm&) override; virtual void initialize(JS::Realm&) override;
@ -37,6 +37,6 @@ private:
GC::Ptr<SubtleCrypto> m_subtle; GC::Ptr<SubtleCrypto> m_subtle;
}; };
WEB_API ErrorOr<String> generate_random_uuid(); WEB_API String generate_random_uuid();
} }

View file

@ -53,7 +53,7 @@ ErrorOr<Utf16String> generate_new_blob_url()
TRY(result.try_append('/')); TRY(result.try_append('/'));
// 9. Generate a UUID [RFC4122] as a string and append it to result. // 9. Generate a UUID [RFC4122] as a string and append it to result.
auto uuid = TRY(Crypto::generate_random_uuid()); auto uuid = Crypto::generate_random_uuid();
TRY(result.try_append(uuid)); TRY(result.try_append(uuid));
// 10. Return result. // 10. Return result.

View file

@ -1685,7 +1685,7 @@ void Navigable::begin_navigation(NavigateParams params)
// NOTE: This step is handled in Navigable::navigate() // NOTE: This step is handled in Navigable::navigate()
// 7. Let navigationId be the result of generating a random UUID. // 7. Let navigationId be the result of generating a random UUID.
String navigation_id = MUST(Crypto::generate_random_uuid()); auto navigation_id = Crypto::generate_random_uuid();
// FIXME: 8. If the surrounding agent is equal to navigable's active document's relevant agent, then continue these steps. // FIXME: 8. If the surrounding agent is equal to navigable's active document's relevant agent, then continue these steps.
// Otherwise, queue a global task on the navigation and traversal task source given navigable's active window to continue these steps. // Otherwise, queue a global task on the navigation and traversal task source given navigable's active window to continue these steps.

View file

@ -25,8 +25,8 @@ void SessionHistoryEntry::visit_edges(Cell::Visitor& visitor)
SessionHistoryEntry::SessionHistoryEntry() SessionHistoryEntry::SessionHistoryEntry()
: m_classic_history_api_state(MUST(structured_serialize_for_storage(vm(), JS::js_null()))) : m_classic_history_api_state(MUST(structured_serialize_for_storage(vm(), JS::js_null())))
, m_navigation_api_state(MUST(structured_serialize_for_storage(vm(), JS::js_undefined()))) , m_navigation_api_state(MUST(structured_serialize_for_storage(vm(), JS::js_undefined())))
, m_navigation_api_key(MUST(Crypto::generate_random_uuid())) , m_navigation_api_key(Crypto::generate_random_uuid())
, m_navigation_api_id(MUST(Crypto::generate_random_uuid())) , m_navigation_api_id(Crypto::generate_random_uuid())
{ {
} }

View file

@ -21,8 +21,8 @@ IDBDatabase::IDBDatabase(JS::Realm& realm, Database& db)
: EventTarget(realm) : EventTarget(realm)
, m_name(db.name()) , m_name(db.name())
, m_associated_database(db) , m_associated_database(db)
, m_uuid(Crypto::generate_random_uuid())
{ {
m_uuid = MUST(Crypto::generate_random_uuid());
db.associate(*this); db.associate(*this);
m_object_store_set = Vector<GC::Ref<ObjectStore>> { db.object_stores() }; m_object_store_set = Vector<GC::Ref<ObjectStore>> { db.object_stores() };
} }

View file

@ -25,8 +25,8 @@ IDBRequest::~IDBRequest() = default;
IDBRequest::IDBRequest(JS::Realm& realm, IDBRequestSource source) IDBRequest::IDBRequest(JS::Realm& realm, IDBRequestSource source)
: EventTarget(realm) : EventTarget(realm)
, m_source(source) , m_source(source)
, m_uuid(Crypto::generate_random_uuid())
{ {
m_uuid = MUST(Crypto::generate_random_uuid());
} }
void IDBRequest::initialize(JS::Realm& realm) void IDBRequest::initialize(JS::Realm& realm)

View file

@ -26,8 +26,8 @@ IDBTransaction::IDBTransaction(JS::Realm& realm, GC::Ref<IDBDatabase> connection
, m_mode(mode) , m_mode(mode)
, m_durability(durability) , m_durability(durability)
, m_scope(move(scopes)) , m_scope(move(scopes))
, m_uuid(Crypto::generate_random_uuid())
{ {
m_uuid = MUST(Crypto::generate_random_uuid());
connection->add_transaction(*this); connection->add_transaction(*this);
} }

View file

@ -1365,7 +1365,7 @@ GC_DEFINE_ALLOCATOR(ActionExecutor);
void wait_for_an_action_queue_token(InputState& input_state) void wait_for_an_action_queue_token(InputState& input_state)
{ {
// 1. Let token be a new unique identifier. // 1. Let token be a new unique identifier.
auto token = MUST(Crypto::generate_random_uuid()); auto token = Crypto::generate_random_uuid();
// 2. Enqueue token in input state's actions queue. // 2. Enqueue token in input state's actions queue.
input_state.actions_queue.append(token); input_state.actions_queue.append(token);

View file

@ -629,7 +629,7 @@ void ViewImplementation::initialize_client(CreateNewClient create_new_client)
m_client_state.client->register_view(m_client_state.page_index, *this); m_client_state.client->register_view(m_client_state.page_index, *this);
} }
m_client_state.client_handle = MUST(Web::Crypto::generate_random_uuid()); m_client_state.client_handle = Web::Crypto::generate_random_uuid();
client().async_set_window_handle(m_client_state.page_index, m_client_state.client_handle); client().async_set_window_handle(m_client_state.page_index, m_client_state.client_handle);
client().async_set_zoom_level(m_client_state.page_index, m_zoom_level); client().async_set_zoom_level(m_client_state.page_index, m_zoom_level);
client().async_set_viewport(m_client_state.page_index, viewport_size(), m_device_pixel_ratio, m_is_fullscreen); client().async_set_viewport(m_client_state.page_index, viewport_size(), m_device_pixel_ratio, m_is_fullscreen);

View file

@ -1716,7 +1716,7 @@ Web::WebDriver::Response WebDriverConnection::element_click_impl(StringView elem
}; };
// 3. Let input id be a the result of generating a UUID. // 3. Let input id be a the result of generating a UUID.
auto input_id = MUST(Web::Crypto::generate_random_uuid()); auto input_id = Web::Crypto::generate_random_uuid();
// 4. Let source be the result of create an input source with input state, and "pointer". // 4. Let source be the result of create an input source with input state, and "pointer".
auto source = Web::WebDriver::create_input_source(input_state, Web::WebDriver::InputSourceType::Pointer, Web::WebDriver::PointerInputSource::Subtype::Mouse); auto source = Web::WebDriver::create_input_source(input_state, Web::WebDriver::InputSourceType::Pointer, Web::WebDriver::PointerInputSource::Subtype::Mouse);
@ -2035,7 +2035,7 @@ Web::WebDriver::Response WebDriverConnection::element_send_keys_impl(StringView
auto& input_state = Web::WebDriver::get_input_state(*current_top_level_browsing_context()); auto& input_state = Web::WebDriver::get_input_state(*current_top_level_browsing_context());
// 10. Let input id be a the result of generating a UUID. // 10. Let input id be a the result of generating a UUID.
auto input_id = MUST(Web::Crypto::generate_random_uuid()); auto input_id = Web::Crypto::generate_random_uuid();
// 11. Let source be the result of create an input source with input state, and "key". // 11. Let source be the result of create an input source with input state, and "key".
auto source = Web::WebDriver::create_input_source(input_state, Web::WebDriver::InputSourceType::Key, {}); auto source = Web::WebDriver::create_input_source(input_state, Web::WebDriver::InputSourceType::Key, {});

View file

@ -35,7 +35,7 @@ static HashMap<String, NonnullRefPtr<Session>> s_http_sessions;
ErrorOr<NonnullRefPtr<Session>> Session::create(NonnullRefPtr<Client> client, JsonObject& capabilities, Web::WebDriver::SessionFlags flags) ErrorOr<NonnullRefPtr<Session>> Session::create(NonnullRefPtr<Client> client, JsonObject& capabilities, Web::WebDriver::SessionFlags flags)
{ {
// 1. Let session id be the result of generating a UUID. // 1. Let session id be the result of generating a UUID.
auto session_id = MUST(Web::Crypto::generate_random_uuid()); auto session_id = Web::Crypto::generate_random_uuid();
// 2. Let session be a new session with session ID session id, and HTTP flag flags contains "http". // 2. Let session be a new session with session ID session id, and HTTP flag flags contains "http".
auto session = adopt_ref(*new Session(client, capabilities, move(session_id), flags)); auto session = adopt_ref(*new Session(client, capabilities, move(session_id), flags));