Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bba288d
refactor(indexeddb): move SafeEncode-related traits and types into th…
mgoldenberg Sep 13, 2025
d795821
refactor(indexeddb): rename IndexeddbSerializer to SafeEncodeSerializer
mgoldenberg Sep 13, 2025
3a86ef2
refactor(indexeddb): rename IndexeddbEventCacheStoreSerializer{Error}…
mgoldenberg Sep 14, 2025
7564411
refactor(indexeddb): rename IndexeddbMediaStoreSerializer{Error} to I…
mgoldenberg Sep 14, 2025
d72e3b3
refactor(indexeddb): deduplicate serializer traits and types
mgoldenberg Sep 16, 2025
5765bf8
refactor(indexeddb): deduplicate async error deps trait
mgoldenberg Sep 16, 2025
b265f9f
refactor(indexeddb): add generalized transaction type and error
mgoldenberg Sep 16, 2025
cdc63b8
refactor(indexeddb): nest generalized transaction in media store tran…
mgoldenberg Sep 16, 2025
078d829
refactor(indexeddb): nest generalized transaction in event cache stor…
mgoldenberg Sep 16, 2025
44c0555
refactor(indexeddb): move custom bool serializer into serializer module
mgoldenberg Sep 16, 2025
eddda13
refactor(indexeddb): deduplicate constants for types from std
mgoldenberg Sep 16, 2025
8abbd1b
refactor(indexeddb): move module-specific constants into their own mo…
mgoldenberg Sep 16, 2025
cb7e221
refactor(indexeddb): rename serializer types modules to indexed_types
mgoldenberg Sep 16, 2025
d44b4d0
refactor(indexeddb): remove extraneous core object store from event c…
mgoldenberg Sep 16, 2025
47b6579
refactor(indexeddb): allow dead code in media store module as it is s…
mgoldenberg Sep 16, 2025
56ea799
refactor(indexeddb): remove unused imports in media store module
mgoldenberg Sep 16, 2025
7c2e936
fix(indexeddb): handle result in event cache store migrations
mgoldenberg Sep 16, 2025
fb9c5d9
refactor(indexeddb): allow dead code in event cache store builder unt…
mgoldenberg Sep 16, 2025
0b04a70
refactor(indexeddb): remove unused imports and dead code from event c…
mgoldenberg Sep 16, 2025
67376c8
refactor(indexeddb): allow dead code in transaction and indexed type …
mgoldenberg Sep 16, 2025
7c530b2
refactor(indexeddb): remove unused imports from transaction module
mgoldenberg Sep 16, 2025
1b77816
feat(indexeddb): add experimental encrypted state events feature to q…
mgoldenberg Sep 16, 2025
96722f7
refactor(indexeddb): expose safe encode trait even when e2e-encryptio…
mgoldenberg Sep 16, 2025
5da4cbc
refactor(indexeddb): tweak features and imports to ensure types and t…
mgoldenberg Sep 16, 2025
3907724
fix(indexeddb): fix import in state store migration tests
mgoldenberg Sep 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions crates/matrix-sdk-indexeddb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ media-store = ["dep:matrix-sdk-base"]
state-store = ["dep:matrix-sdk-base", "growable-bloom-filter"]
e2e-encryption = ["dep:matrix-sdk-crypto"]
testing = ["matrix-sdk-crypto?/testing"]
experimental-encrypted-state-events = [
"matrix-sdk-crypto?/experimental-encrypted-state-events"
]

[dependencies]
anyhow.workspace = true
Expand Down
14 changes: 7 additions & 7 deletions crates/matrix-sdk-indexeddb/src/crypto_store/migrations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use indexed_db_futures::{prelude::*, web_sys::DomException};
use tracing::info;
use wasm_bindgen::JsValue;

use crate::{crypto_store::Result, serializer::IndexeddbSerializer, IndexeddbCryptoStoreError};
use crate::{crypto_store::Result, serializer::SafeEncodeSerializer, IndexeddbCryptoStoreError};

mod old_keys;
mod v0_to_v5;
Expand Down Expand Up @@ -100,7 +100,7 @@ const MAX_SUPPORTED_SCHEMA_VERSION: u32 = 99;
/// of the schema if necessary.
pub async fn open_and_upgrade_db(
name: &str,
serializer: &IndexeddbSerializer,
serializer: &SafeEncodeSerializer,
) -> Result<IdbDatabase, IndexeddbCryptoStoreError> {
// Move the DB version up from where it is to the latest version.
//
Expand Down Expand Up @@ -282,7 +282,7 @@ mod tests {
#[async_test]
async fn test_count_lots_of_sessions_v8() {
let cipher = Arc::new(StoreCipher::new().unwrap());
let serializer = IndexeddbSerializer::new(Some(cipher.clone()));
let serializer = SafeEncodeSerializer::new(Some(cipher.clone()));
// Session keys are slow to create, so make one upfront and use it for every
// session
let session_key = create_session_key();
Expand Down Expand Up @@ -319,7 +319,7 @@ mod tests {
/// Make lots of sessions and see how long it takes to count them in v10
#[async_test]
async fn test_count_lots_of_sessions_v10() {
let serializer = IndexeddbSerializer::new(Some(Arc::new(StoreCipher::new().unwrap())));
let serializer = SafeEncodeSerializer::new(Some(Arc::new(StoreCipher::new().unwrap())));

// Session keys are slow to create, so make one upfront and use it for every
// session
Expand Down Expand Up @@ -395,7 +395,7 @@ mod tests {
i: usize,
session_key: &SessionKey,
cipher: &Arc<StoreCipher>,
serializer: &IndexeddbSerializer,
serializer: &SafeEncodeSerializer,
) -> (JsValue, JsValue) {
let session = create_inbound_group_session(i, session_key);
let pickled_session = session.pickle().await;
Expand All @@ -416,7 +416,7 @@ mod tests {
async fn create_inbound_group_sessions3_record(
i: usize,
session_key: &SessionKey,
serializer: &IndexeddbSerializer,
serializer: &SafeEncodeSerializer,
) -> (JsValue, JsValue) {
let session = create_inbound_group_session(i, session_key);
let pickled_session = session.pickle().await;
Expand Down Expand Up @@ -682,7 +682,7 @@ mod tests {
// entry.
let db = create_v5_db(&db_name).await.unwrap();

let serializer = IndexeddbSerializer::new(store_cipher.clone());
let serializer = SafeEncodeSerializer::new(store_cipher.clone());

let txn = db
.transaction_on_one_with_mode(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ use crate::{
keys,
migrations::{do_schema_upgrade, old_keys, MigrationDb},
},
serializer::IndexeddbSerializer,
serializer::SafeEncodeSerializer,
};

/// Migrate data from `backup_keys.backup_key_v1` to
/// `backup_keys.backup_version_v1`.
pub(crate) async fn data_migrate(
name: &str,
serializer: &IndexeddbSerializer,
serializer: &SafeEncodeSerializer,
) -> crate::crypto_store::Result<()> {
let db = MigrationDb::new(name, 11).await?;
let txn = db.transaction_on_one_with_mode(keys::BACKUP_KEYS, IdbTransactionMode::Readwrite)?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ use web_sys::{DomException, IdbTransactionMode};
use super::MigrationDb;
use crate::{
crypto_store::{keys, migrations::do_schema_upgrade, Result},
serializer::IndexeddbSerializer,
serializer::SafeEncodeSerializer,
};

pub(crate) async fn data_migrate(name: &str, _: &IndexeddbSerializer) -> Result<()> {
pub(crate) async fn data_migrate(name: &str, _: &SafeEncodeSerializer) -> Result<()> {
let db = MigrationDb::new(name, 14).await?;
let transaction = db.transaction_on_one_with_mode(
keys::RECEIVED_ROOM_KEY_BUNDLES,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use crate::{
migrations::{add_nonunique_index, do_schema_upgrade, old_keys, v7, MigrationDb},
Result,
},
serializer::IndexeddbSerializer,
serializer::SafeEncodeSerializer,
IndexeddbCryptoStoreError,
};

Expand All @@ -51,7 +51,7 @@ pub(crate) async fn schema_add(name: &str) -> Result<(), DomException> {
}

/// Migrate data from `inbound_group_sessions` into `inbound_group_sessions2`.
pub(crate) async fn data_migrate(name: &str, serializer: &IndexeddbSerializer) -> Result<()> {
pub(crate) async fn data_migrate(name: &str, serializer: &SafeEncodeSerializer) -> Result<()> {
let db = MigrationDb::new(name, 7).await?;

// The new store has been made for inbound group sessions; time to populate it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub struct InboundGroupSessionIndexedDbObject2 {
#[serde(
default,
skip_serializing_if = "std::ops::Not::not",
with = "crate::serialize_bool_for_indexeddb"
with = "crate::serializer::foreign::bool"
)]
pub needs_backup: bool,
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ use crate::{
migrations::{do_schema_upgrade, old_keys, v7, MigrationDb},
Result,
},
serializer::IndexeddbSerializer,
serializer::SafeEncodeSerializer,
IndexeddbCryptoStoreError,
};

/// In the migration v5 to v7, we incorrectly copied the keys in
/// `inbound_group_sessions` verbatim into `inbound_group_sessions2`. What we
/// should have done is re-hash them using the new table name, so we fix them up
/// here.
pub(crate) async fn data_migrate(name: &str, serializer: &IndexeddbSerializer) -> Result<()> {
pub(crate) async fn data_migrate(name: &str, serializer: &SafeEncodeSerializer) -> Result<()> {
let db = MigrationDb::new(name, 8).await?;

let txn = db.transaction_on_one_with_mode(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::{
},
InboundGroupSessionIndexedDbObject, Result,
},
serializer::IndexeddbSerializer,
serializer::SafeEncodeSerializer,
IndexeddbCryptoStoreError,
};

Expand Down Expand Up @@ -59,7 +59,7 @@ pub(crate) async fn schema_add(name: &str) -> Result<(), DomException> {
}

/// Migrate data from `inbound_group_sessions2` into `inbound_group_sessions3`.
pub(crate) async fn data_migrate(name: &str, serializer: &IndexeddbSerializer) -> Result<()> {
pub(crate) async fn data_migrate(name: &str, serializer: &SafeEncodeSerializer) -> Result<()> {
let db = MigrationDb::new(name, 10).await?;

let txn = db.transaction_on_multi_with_mode(
Expand Down
26 changes: 13 additions & 13 deletions crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ use web_sys::IdbKeyRange;

use crate::{
crypto_store::migrations::open_and_upgrade_db,
serializer::{IndexeddbSerializer, IndexeddbSerializerError, MaybeEncrypted},
serializer::{MaybeEncrypted, SafeEncodeSerializer, SafeEncodeSerializerError},
};

mod migrations;
Expand Down Expand Up @@ -122,7 +122,7 @@ pub struct IndexeddbCryptoStore {
name: String,
pub(crate) inner: IdbDatabase,

serializer: IndexeddbSerializer,
serializer: SafeEncodeSerializer,
save_changes_lock: Arc<Mutex<()>>,
}

Expand Down Expand Up @@ -155,14 +155,14 @@ pub enum IndexeddbCryptoStoreError {
SchemaTooNewError { max_supported_version: u32, current_version: u32 },
}

impl From<IndexeddbSerializerError> for IndexeddbCryptoStoreError {
fn from(value: IndexeddbSerializerError) -> Self {
impl From<SafeEncodeSerializerError> for IndexeddbCryptoStoreError {
fn from(value: SafeEncodeSerializerError) -> Self {
match value {
IndexeddbSerializerError::Serialization(error) => Self::Serialization(error),
IndexeddbSerializerError::DomException { code, name, message } => {
SafeEncodeSerializerError::Serialization(error) => Self::Serialization(error),
SafeEncodeSerializerError::DomException { code, name, message } => {
Self::DomException { code, name, message }
}
IndexeddbSerializerError::CryptoStoreError(crypto_store_error) => {
SafeEncodeSerializerError::CryptoStoreError(crypto_store_error) => {
Self::CryptoStoreError(crypto_store_error)
}
}
Expand Down Expand Up @@ -287,7 +287,7 @@ impl IndexeddbCryptoStore {
) -> Result<Self> {
let name = format!("{prefix:0}::matrix-sdk-crypto");

let serializer = IndexeddbSerializer::new(store_cipher);
let serializer = SafeEncodeSerializer::new(store_cipher);
debug!("IndexedDbCryptoStore: opening main store {name}");
let db = open_and_upgrade_db(&name, &serializer).await?;

Expand Down Expand Up @@ -1733,7 +1733,7 @@ struct GossipRequestIndexedDbObject {
#[serde(
default,
skip_serializing_if = "std::ops::Not::not",
with = "crate::serialize_bool_for_indexeddb"
with = "crate::serializer::foreign::bool"
)]
unsent: bool,
}
Expand Down Expand Up @@ -1766,7 +1766,7 @@ struct InboundGroupSessionIndexedDbObject {
#[serde(
default,
skip_serializing_if = "std::ops::Not::not",
with = "crate::serialize_bool_for_indexeddb"
with = "crate::serializer::foreign::bool"
)]
needs_backup: bool,

Expand Down Expand Up @@ -1804,7 +1804,7 @@ impl InboundGroupSessionIndexedDbObject {
/// session.
pub async fn from_session(
session: &InboundGroupSession,
serializer: &IndexeddbSerializer,
serializer: &SafeEncodeSerializer,
) -> Result<Self, CryptoStoreError> {
let session_id =
serializer.encode_key_as_string(keys::INBOUND_GROUP_SESSIONS_V3, session.session_id());
Expand Down Expand Up @@ -1837,7 +1837,7 @@ mod unit_tests {
use ruma::{device_id, room_id, user_id};

use super::InboundGroupSessionIndexedDbObject;
use crate::serializer::{IndexeddbSerializer, MaybeEncrypted};
use crate::serializer::{MaybeEncrypted, SafeEncodeSerializer};

#[test]
fn needs_backup_is_serialized_as_a_u8_in_json() {
Expand Down Expand Up @@ -1915,7 +1915,7 @@ mod unit_tests {
)
.unwrap();

InboundGroupSessionIndexedDbObject::from_session(&session, &IndexeddbSerializer::new(None))
InboundGroupSessionIndexedDbObject::from_session(&session, &SafeEncodeSerializer::new(None))
.await
.unwrap()
}
Expand Down
23 changes: 23 additions & 0 deletions crates/matrix-sdk-indexeddb/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2025 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License

use matrix_sdk_base::{SendOutsideWasm, SyncOutsideWasm};

/// A trait that combines the necessary traits needed for asynchronous runtimes,
/// but excludes them when running in a web environment - i.e., when
/// `#[cfg(target_family = "wasm")]`.
pub trait AsyncErrorDeps: std::error::Error + SendOutsideWasm + SyncOutsideWasm + 'static {}

impl<T> AsyncErrorDeps for T where T: std::error::Error + SendOutsideWasm + SyncOutsideWasm + 'static
{}
13 changes: 8 additions & 5 deletions crates/matrix-sdk-indexeddb/src/event_cache_store/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License

// At the moment, this builder is not public outside of the crate, so we
// get a few dead code warnings; however, this will eventually be a public
// type, at which point the line below can be removed.
#![allow(dead_code)]

use std::{rc::Rc, sync::Arc};

use matrix_sdk_store_encryption::StoreCipher;

use crate::{
event_cache_store::{
error::IndexeddbEventCacheStoreError, migrations::open_and_upgrade_db,
serializer::IndexeddbEventCacheStoreSerializer, IndexeddbEventCacheStore,
IndexeddbEventCacheStore,
},
serializer::IndexeddbSerializer,
serializer::{IndexedTypeSerializer, SafeEncodeSerializer},
};

/// A type for conveniently building an [`IndexeddbEventCacheStore`]
Expand Down Expand Up @@ -65,9 +70,7 @@ impl IndexeddbEventCacheStoreBuilder {
pub async fn build(self) -> Result<IndexeddbEventCacheStore, IndexeddbEventCacheStoreError> {
Ok(IndexeddbEventCacheStore {
inner: Rc::new(open_and_upgrade_db(&self.database_name).await?),
serializer: IndexeddbEventCacheStoreSerializer::new(IndexeddbSerializer::new(
self.store_cipher,
)),
serializer: IndexedTypeSerializer::new(SafeEncodeSerializer::new(self.store_cipher)),
})
}
}
27 changes: 16 additions & 11 deletions crates/matrix-sdk-indexeddb/src/event_cache_store/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License

use matrix_sdk_base::{event_cache::store::EventCacheStoreError, SendOutsideWasm, SyncOutsideWasm};
use matrix_sdk_base::event_cache::store::EventCacheStoreError;
use serde::de::Error;
use thiserror::Error;

use crate::event_cache_store::transaction::IndexeddbEventCacheStoreTransactionError;

/// A trait that combines the necessary traits needed for asynchronous runtimes,
/// but excludes them when running in a web environment - i.e., when
/// `#[cfg(target_family = "wasm")]`.
pub trait AsyncErrorDeps: std::error::Error + SendOutsideWasm + SyncOutsideWasm + 'static {}

impl<T> AsyncErrorDeps for T where T: std::error::Error + SendOutsideWasm + SyncOutsideWasm + 'static
{}
use crate::transaction::TransactionError;

#[derive(Debug, Error)]
pub enum IndexeddbEventCacheStoreError {
Expand All @@ -38,7 +31,7 @@ pub enum IndexeddbEventCacheStoreError {
#[error("no max chunk id")]
NoMaxChunkId,
#[error("transaction: {0}")]
Transaction(#[from] IndexeddbEventCacheStoreTransactionError),
Transaction(#[from] TransactionError),
}

impl From<web_sys::DomException> for IndexeddbEventCacheStoreError {
Expand All @@ -65,3 +58,15 @@ impl From<IndexeddbEventCacheStoreError> for EventCacheStoreError {
}
}
}

impl From<TransactionError> for EventCacheStoreError {
fn from(value: TransactionError) -> Self {
use TransactionError::*;

match value {
DomException { .. } => Self::InvalidData { details: value.to_string() },
Serialization(e) => Self::Serialization(serde_json::Error::custom(e.to_string())),
ItemIsNotUnique | ItemNotFound => Self::InvalidData { details: value.to_string() },
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ use matrix_sdk_base::{
use matrix_sdk_test::DEFAULT_TEST_ROOM_ID;
use ruma::room_id;

use crate::event_cache_store::{
transaction::IndexeddbEventCacheStoreTransactionError, IndexeddbEventCacheStore,
IndexeddbEventCacheStoreError,
use crate::{
event_cache_store::{IndexeddbEventCacheStore, IndexeddbEventCacheStoreError},
transaction::TransactionError,
};

pub async fn test_linked_chunk_new_items_chunk(store: IndexeddbEventCacheStore) {
Expand Down Expand Up @@ -423,9 +423,7 @@ pub async fn test_linked_chunk_update_is_a_transaction(store: IndexeddbEventCach
// The operation fails with a constraint violation error.
assert_matches!(
err,
IndexeddbEventCacheStoreError::Transaction(
IndexeddbEventCacheStoreTransactionError::DomException { .. }
)
IndexeddbEventCacheStoreError::Transaction(TransactionError::DomException { .. })
);

// If the updates have been handled transactionally, then no new chunks should
Expand Down
Loading
Loading