servo/components/shared/net/indexeddb_thread.rs
Ashwin Naren a91625a332
Proper compare implementation for IndexedDBKeyType (#38123)
This is needed by the IndexedDBKeyRange implementation.

Doesn't fix anything since it isn't used at the moment.

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
2025-07-18 06:26:47 +00:00

304 lines
10 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cmp::{PartialEq, PartialOrd};
use ipc_channel::ipc::IpcSender;
use serde::{Deserialize, Serialize};
use servo_url::origin::ImmutableOrigin;
// https://www.w3.org/TR/IndexedDB-2/#enumdef-idbtransactionmode
#[derive(Debug, Deserialize, Serialize)]
pub enum IndexedDBTxnMode {
Readonly,
Readwrite,
Versionchange,
}
/// <https://www.w3.org/TR/IndexedDB-2/#key-type>
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum IndexedDBKeyType {
Number(f64),
String(String),
Binary(Vec<u8>),
// FIXME:(arihant2math) Date should not be stored as a Vec<u8>
Date(Vec<u8>),
Array(Vec<IndexedDBKeyType>),
// FIXME:(arihant2math) implment ArrayBuffer
}
/// <https://www.w3.org/TR/IndexedDB-2/#compare-two-keys>
impl PartialOrd for IndexedDBKeyType {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
// 1. Let ta be the type of a.
// 2. Let tb be the type of b.
match (self, other) {
// Step 3: If ta is array and tb is binary, string, date or number, return 1.
(
IndexedDBKeyType::Array(_),
IndexedDBKeyType::Binary(_) |
IndexedDBKeyType::Date(_) |
IndexedDBKeyType::Number(_) |
IndexedDBKeyType::String(_),
) => Some(std::cmp::Ordering::Greater),
// Step 4: If tb is array and ta is binary, string, date or number, return -1.
(
IndexedDBKeyType::Binary(_) |
IndexedDBKeyType::Date(_) |
IndexedDBKeyType::Number(_) |
IndexedDBKeyType::String(_),
IndexedDBKeyType::Array(_),
) => Some(std::cmp::Ordering::Less),
// Step 5: If ta is binary and tb is string, date or number, return 1.
(
IndexedDBKeyType::Binary(_),
IndexedDBKeyType::String(_) |
IndexedDBKeyType::Date(_) |
IndexedDBKeyType::Number(_),
) => Some(std::cmp::Ordering::Greater),
// Step 6: If tb is binary and ta is string, date or number, return -1.
(
IndexedDBKeyType::String(_) |
IndexedDBKeyType::Date(_) |
IndexedDBKeyType::Number(_),
IndexedDBKeyType::Binary(_),
) => Some(std::cmp::Ordering::Less),
// Step 7: If ta is string and tb is date or number, return 1.
(
IndexedDBKeyType::String(_),
IndexedDBKeyType::Date(_) | IndexedDBKeyType::Number(_),
) => Some(std::cmp::Ordering::Greater),
// Step 8: If tb is string and ta is date or number, return -1.
(
IndexedDBKeyType::Date(_) | IndexedDBKeyType::Number(_),
IndexedDBKeyType::String(_),
) => Some(std::cmp::Ordering::Less),
// Step 9: If ta is date and tb is number, return 1.
(IndexedDBKeyType::Date(_), IndexedDBKeyType::Number(_)) => {
Some(std::cmp::Ordering::Greater)
},
// Step 10: If tb is date and ta is number, return -1.
(IndexedDBKeyType::Number(_), IndexedDBKeyType::Date(_)) => {
Some(std::cmp::Ordering::Less)
},
// Step 11 skipped
// TODO: Likely a tiny bit wrong (use js number comparison)
(IndexedDBKeyType::Number(a), IndexedDBKeyType::Number(b)) => a.partial_cmp(b),
// TODO: Likely a tiny bit wrong (use js string comparison)
(IndexedDBKeyType::String(a), IndexedDBKeyType::String(b)) => a.partial_cmp(b),
// TODO: Likely a little wrong (use js binary comparison)
(IndexedDBKeyType::Binary(a), IndexedDBKeyType::Binary(b)) => a.partial_cmp(b),
// TODO: Very wrong (convert to Date and compare)
(IndexedDBKeyType::Date(a), IndexedDBKeyType::Date(b)) => a.partial_cmp(b),
// TODO: Probably also wrong (the items in a and b should be compared, double check against the spec)
(IndexedDBKeyType::Array(a), IndexedDBKeyType::Array(b)) => a.partial_cmp(b),
// No catch-all is used, rust ensures that all variants are handled
}
}
}
impl PartialEq for IndexedDBKeyType {
fn eq(&self, other: &Self) -> bool {
let cmp = self.partial_cmp(other);
match cmp {
Some(std::cmp::Ordering::Equal) => true,
Some(std::cmp::Ordering::Less) | Some(std::cmp::Ordering::Greater) => false,
None => {
// If we can't compare the two keys, we assume they are not equal.
false
},
}
}
}
// <https://www.w3.org/TR/IndexedDB-2/#key-range>
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[allow(unused)]
pub struct IndexedDBKeyRange {
pub lower: Option<IndexedDBKeyType>,
pub upper: Option<IndexedDBKeyType>,
pub lower_open: bool,
pub upper_open: bool,
}
impl From<IndexedDBKeyType> for IndexedDBKeyRange {
fn from(key: IndexedDBKeyType) -> Self {
IndexedDBKeyRange {
lower: Some(key.clone()),
upper: Some(key),
..Default::default()
}
}
}
impl IndexedDBKeyRange {
// <https://www.w3.org/TR/IndexedDB-2/#in>
pub fn contains(&self, key: &IndexedDBKeyType) -> bool {
// A key is in a key range if both of the following conditions are fulfilled:
// The lower bound is null, or it is less than key,
// or it is both equal to key and the lower open flag is unset.
// The upper bound is null, or it is greater than key,
// or it is both equal to key and the upper open flag is unset
let lower_bound_condition = self
.lower
.as_ref()
.is_none_or(|lower| lower < key || (!self.lower_open && lower == key));
let upper_bound_condition = self
.upper
.as_ref()
.is_none_or(|upper| key < upper || (!self.upper_open && key == upper));
lower_bound_condition && upper_bound_condition
}
}
#[derive(Debug, Deserialize, Serialize)]
pub enum AsyncReadOnlyOperation {
/// Gets the value associated with the given key in the associated idb data
GetItem(
IndexedDBKeyType, // Key
),
Count(
IndexedDBKeyType, // Key
),
}
#[derive(Debug, Deserialize, Serialize)]
pub enum AsyncReadWriteOperation {
/// Sets the value of the given key in the associated idb data
PutItem(
IndexedDBKeyType, // Key
Vec<u8>, // Value
bool, // Should overwrite
),
/// Removes the key/value pair for the given key in the associated idb data
RemoveItem(
IndexedDBKeyType, // Key
),
/// Clears all key/value pairs in the associated idb data
Clear,
}
/// Operations that are not executed instantly, but rather added to a
/// queue that is eventually run.
#[derive(Debug, Deserialize, Serialize)]
pub enum AsyncOperation {
ReadOnly(AsyncReadOnlyOperation),
ReadWrite(AsyncReadWriteOperation),
}
#[derive(Debug, Deserialize, Serialize)]
pub enum SyncOperation {
/// Upgrades the version of the database
UpgradeVersion(
IpcSender<Result<u64, ()>>,
ImmutableOrigin,
String, // Database
u64, // Serial number for the transaction
u64, // Version to upgrade to
),
/// Checks if an object store has a key generator, used in e.g. Put
HasKeyGenerator(
IpcSender<bool>,
ImmutableOrigin,
String, // Database
String, // Store
),
/// Commits changes of a transaction to the database
Commit(
IpcSender<Result<(), ()>>,
ImmutableOrigin,
String, // Database
u64, // Transaction serial number
),
/// Creates a new store for the database
CreateObjectStore(
IpcSender<Result<(), ()>>,
ImmutableOrigin,
String, // Database
String, // Store
bool,
),
DeleteObjectStore(
IpcSender<Result<(), ()>>,
ImmutableOrigin,
String, // Database
String, // Store
),
CloseDatabase(
IpcSender<Result<(), ()>>,
ImmutableOrigin,
String, // Database
),
OpenDatabase(
IpcSender<u64>, // Returns the version
ImmutableOrigin,
String, // Database
Option<u64>, // Eventual version
),
/// Deletes the database
DeleteDatabase(
IpcSender<Result<(), ()>>,
ImmutableOrigin,
String, // Database
),
/// Returns an unique identifier that is used to be able to
/// commit/abort transactions.
RegisterNewTxn(
IpcSender<u64>,
ImmutableOrigin,
String, // Database
),
/// Starts executing the requests of a transaction
/// <https://www.w3.org/TR/IndexedDB-2/#transaction-start>
StartTransaction(
IpcSender<Result<(), ()>>,
ImmutableOrigin,
String, // Database
u64, // The serial number of the mutating transaction
),
/// Returns the version of the database
Version(
IpcSender<u64>,
ImmutableOrigin,
String, // Database
),
/// Send a reply when done cleaning up thread resources and then shut it down
Exit(IpcSender<()>),
}
/// The set of all kinds of results that can be returned from async operations.
#[derive(Debug, Deserialize, Serialize)]
pub enum IdbResult {
/// The key used to perform an async operation.
Key(IndexedDBKeyType),
/// A structured clone of a value retrieved from an object store.
Data(Vec<u8>),
}
#[derive(Debug, Deserialize, Serialize)]
pub enum IndexedDBThreadMsg {
Sync(SyncOperation),
Async(
IpcSender<Result<Option<IdbResult>, ()>>, // Sender to send the result of the async operation
ImmutableOrigin,
String, // Database
String, // ObjectStore
u64, // Serial number of the transaction that requests this operation
IndexedDBTxnMode,
AsyncOperation,
),
}