cbf-compositor 0.1.0-alpha.1

Scene-based browser surface compositor for CBF applications.
Documentation
use std::collections::HashMap;

use cbf::data::{
    ids::{BrowsingContextId, TransientBrowsingContextId},
    transient_browsing_context::TransientBrowsingContextKind,
};

use crate::model::TransientOwnership;

#[derive(Debug, Default)]
pub(crate) struct OwnershipState {
    transients: HashMap<TransientBrowsingContextId, TransientOwnership>,
}

impl OwnershipState {
    pub(crate) fn upsert(
        &mut self,
        transient_browsing_context_id: TransientBrowsingContextId,
        parent_browsing_context_id: BrowsingContextId,
        kind: TransientBrowsingContextKind,
    ) {
        self.transients.insert(
            transient_browsing_context_id,
            TransientOwnership {
                transient_browsing_context_id,
                parent_browsing_context_id,
                kind,
            },
        );
    }

    #[allow(dead_code)]
    pub(crate) fn get(
        &self,
        transient_browsing_context_id: TransientBrowsingContextId,
    ) -> Option<TransientOwnership> {
        self.transients.get(&transient_browsing_context_id).copied()
    }

    pub(crate) fn remove(
        &mut self,
        transient_browsing_context_id: TransientBrowsingContextId,
    ) -> Option<TransientOwnership> {
        self.transients.remove(&transient_browsing_context_id)
    }

    pub(crate) fn remove_by_parent(
        &mut self,
        parent_browsing_context_id: BrowsingContextId,
    ) -> Vec<TransientBrowsingContextId> {
        let transient_ids = self
            .transients
            .values()
            .filter_map(|ownership| {
                (ownership.parent_browsing_context_id == parent_browsing_context_id)
                    .then_some(ownership.transient_browsing_context_id)
            })
            .collect::<Vec<_>>();

        for transient_id in &transient_ids {
            self.transients.remove(transient_id);
        }

        transient_ids
    }
}

#[cfg(test)]
mod tests {
    use cbf::data::{
        ids::{BrowsingContextId, TransientBrowsingContextId},
        transient_browsing_context::TransientBrowsingContextKind,
    };

    use super::OwnershipState;

    #[test]
    fn remove_by_parent_only_removes_owned_transients() {
        let mut state = OwnershipState::default();
        state.upsert(
            TransientBrowsingContextId::new(1),
            BrowsingContextId::new(10),
            TransientBrowsingContextKind::Popup,
        );
        state.upsert(
            TransientBrowsingContextId::new(2),
            BrowsingContextId::new(11),
            TransientBrowsingContextKind::ToolWindow,
        );

        let removed = state.remove_by_parent(BrowsingContextId::new(10));

        assert_eq!(removed, vec![TransientBrowsingContextId::new(1)]);
        assert!(state.get(TransientBrowsingContextId::new(1)).is_none());
        assert!(state.get(TransientBrowsingContextId::new(2)).is_some());
    }
}