Add privacy local data inventory
This commit is contained in:
@@ -16,6 +16,7 @@ mod notes_markdown;
|
|||||||
mod plugin_catalog;
|
mod plugin_catalog;
|
||||||
mod plugin_details;
|
mod plugin_details;
|
||||||
mod plugins;
|
mod plugins;
|
||||||
|
mod privacy_data_inventory;
|
||||||
mod privacy_security;
|
mod privacy_security;
|
||||||
mod profiles;
|
mod profiles;
|
||||||
mod reading_list;
|
mod reading_list;
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
use ely_browser_core::{BrowserSnapshot, LocalDataInventory};
|
||||||
|
use ely_design_system::colors;
|
||||||
|
use gpui::{AnyElement, IntoElement, ParentElement, Styled, div, rgb};
|
||||||
|
use gpui_component::{IconName, StyledExt};
|
||||||
|
|
||||||
|
pub(super) fn render_local_data_inventory(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||||
|
let inventory = snapshot.local_data_inventory;
|
||||||
|
|
||||||
|
div()
|
||||||
|
.rounded_md()
|
||||||
|
.border_1()
|
||||||
|
.border_color(rgb(colors::HAIRLINE))
|
||||||
|
.bg(rgb(colors::CANVAS_SOFT))
|
||||||
|
.px_4()
|
||||||
|
.py_3()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap_3()
|
||||||
|
.child(render_inventory_header(snapshot, inventory))
|
||||||
|
.child(render_inventory_rows(inventory))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_inventory_header(
|
||||||
|
snapshot: &BrowserSnapshot,
|
||||||
|
inventory: LocalDataInventory,
|
||||||
|
) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.justify_between()
|
||||||
|
.gap_4()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.min_w_0()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.gap_3()
|
||||||
|
.child(div().text_color(rgb(colors::PRIMARY)).child(IconName::Inspector))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.min_w_0()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap_1()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_sm()
|
||||||
|
.font_semibold()
|
||||||
|
.text_color(rgb(colors::INK))
|
||||||
|
.child("Local Data"),
|
||||||
|
)
|
||||||
|
.child(div().text_xs().truncate().text_color(rgb(colors::MUTED)).child(
|
||||||
|
format!(
|
||||||
|
"{} Profile inventory for review, export, and deletion.",
|
||||||
|
snapshot.active_profile_name
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex_none()
|
||||||
|
.rounded_md()
|
||||||
|
.border_1()
|
||||||
|
.border_color(rgb(colors::HAIRLINE))
|
||||||
|
.bg(rgb(colors::CANVAS))
|
||||||
|
.px_3()
|
||||||
|
.py_2()
|
||||||
|
.text_xs()
|
||||||
|
.font_semibold()
|
||||||
|
.text_color(rgb(colors::INK))
|
||||||
|
.child(format!("{} items", inventory.total_items())),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_inventory_rows(inventory: LocalDataInventory) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.child(inventory_row(IconName::Globe, "Open Tabs", inventory.open_tabs()))
|
||||||
|
.child(inventory_row(IconName::Inbox, "Archived Tabs", inventory.archived_tabs()))
|
||||||
|
.child(inventory_row(IconName::Undo2, "History", inventory.history_entries()))
|
||||||
|
.child(inventory_row(IconName::BookOpen, "Bookmarks", inventory.bookmarks()))
|
||||||
|
.child(inventory_row(IconName::File, "Notes", inventory.notes()))
|
||||||
|
.child(inventory_row(IconName::CircleCheck, "Reading List", inventory.reading_list()))
|
||||||
|
.child(inventory_row(IconName::Folder, "Downloads", inventory.downloads()))
|
||||||
|
.child(inventory_row(IconName::Eye, "Site Permissions", inventory.site_permissions()))
|
||||||
|
.child(inventory_row(
|
||||||
|
IconName::Inspector,
|
||||||
|
"Audit Events",
|
||||||
|
inventory.site_permission_audit_events(),
|
||||||
|
))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inventory_row(icon: IconName, label: &'static str, count: usize) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.py_2()
|
||||||
|
.border_t_1()
|
||||||
|
.border_color(rgb(colors::HAIRLINE))
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.justify_between()
|
||||||
|
.gap_4()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.min_w_0()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.gap_3()
|
||||||
|
.child(div().text_color(rgb(colors::MUTED_SOFT)).child(icon))
|
||||||
|
.child(
|
||||||
|
div().min_w_0().truncate().text_sm().text_color(rgb(colors::INK)).child(label),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex_none()
|
||||||
|
.text_sm()
|
||||||
|
.font_semibold()
|
||||||
|
.text_color(rgb(colors::MUTED))
|
||||||
|
.child(count.to_string()),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ use gpui_component::{
|
|||||||
scroll::ScrollableElement,
|
scroll::ScrollableElement,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{ElyShell, render_canvas_surface};
|
use super::{ElyShell, privacy_data_inventory::render_local_data_inventory, render_canvas_surface};
|
||||||
|
|
||||||
impl ElyShell {
|
impl ElyShell {
|
||||||
pub(super) fn render_privacy_security_page(
|
pub(super) fn render_privacy_security_page(
|
||||||
@@ -32,6 +32,7 @@ impl ElyShell {
|
|||||||
.when(snapshot.active_profile_history_entry_count > 0, |this| {
|
.when(snapshot.active_profile_history_entry_count > 0, |this| {
|
||||||
this.child(render_history_clear_controls(confirming_clear, cx))
|
this.child(render_history_clear_controls(confirming_clear, cx))
|
||||||
})
|
})
|
||||||
|
.child(render_local_data_inventory(snapshot))
|
||||||
.child(render_privacy_settings_rows(snapshot, cx)),
|
.child(render_privacy_settings_rows(snapshot, cx)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ pub use error::CoreError;
|
|||||||
pub use state::{
|
pub use state::{
|
||||||
BookmarkImportSummary, BrowserCore, BrowserSnapshot, ELYBOOKMARKS_FILE_EXTENSION,
|
BookmarkImportSummary, BrowserCore, BrowserSnapshot, ELYBOOKMARKS_FILE_EXTENSION,
|
||||||
ELYBOOKMARKS_SCHEMA_VERSION, ELYSPACE_FILE_EXTENSION, ELYSPACE_SCHEMA_VERSION,
|
ELYBOOKMARKS_SCHEMA_VERSION, ELYSPACE_FILE_EXTENSION, ELYSPACE_SCHEMA_VERSION,
|
||||||
ElyBookmarksPackage, ElySpacePackage, InitialBrowserConfig, InstalledPlugin, PluginAuditAction,
|
ElyBookmarksPackage, ElySpacePackage, InitialBrowserConfig, InstalledPlugin,
|
||||||
PluginAuditEvent, SiteDataClearance, SpaceImportProfileMapping, TrashedSpace,
|
LocalDataInventory, PluginAuditAction, PluginAuditEvent, SiteDataClearance,
|
||||||
|
SpaceImportProfileMapping, TrashedSpace,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ pub use bookmarks::{
|
|||||||
ElyBookmarksPackage,
|
ElyBookmarksPackage,
|
||||||
};
|
};
|
||||||
pub use plugins::{InstalledPlugin, PluginAuditAction, PluginAuditEvent};
|
pub use plugins::{InstalledPlugin, PluginAuditAction, PluginAuditEvent};
|
||||||
|
pub use privacy::LocalDataInventory;
|
||||||
pub use site_data::SiteDataClearance;
|
pub use site_data::SiteDataClearance;
|
||||||
pub use space_exports::{
|
pub use space_exports::{
|
||||||
ELYSPACE_FILE_EXTENSION, ELYSPACE_SCHEMA_VERSION, ElySpacePackage, SpaceImportProfileMapping,
|
ELYSPACE_FILE_EXTENSION, ELYSPACE_SCHEMA_VERSION, ElySpacePackage, SpaceImportProfileMapping,
|
||||||
@@ -97,6 +98,7 @@ pub struct BrowserSnapshot {
|
|||||||
pub download_entries: Vec<DownloadEntry>,
|
pub download_entries: Vec<DownloadEntry>,
|
||||||
pub history_entries: Vec<HistoryEntry>,
|
pub history_entries: Vec<HistoryEntry>,
|
||||||
pub active_profile_history_entry_count: usize,
|
pub active_profile_history_entry_count: usize,
|
||||||
|
pub local_data_inventory: LocalDataInventory,
|
||||||
pub tab_groups: Vec<TabGroup>,
|
pub tab_groups: Vec<TabGroup>,
|
||||||
pub split_layouts: Vec<SplitLayout>,
|
pub split_layouts: Vec<SplitLayout>,
|
||||||
pub installed_plugins: Vec<InstalledPlugin>,
|
pub installed_plugins: Vec<InstalledPlugin>,
|
||||||
@@ -385,6 +387,7 @@ impl BrowserCore {
|
|||||||
download_entries: self.visible_downloads(),
|
download_entries: self.visible_downloads(),
|
||||||
history_entries: self.visible_history(),
|
history_entries: self.visible_history(),
|
||||||
active_profile_history_entry_count: self.active_profile_history_count(),
|
active_profile_history_entry_count: self.active_profile_history_count(),
|
||||||
|
local_data_inventory: self.active_profile_local_data_inventory(),
|
||||||
tab_groups: self.visible_tab_groups(),
|
tab_groups: self.visible_tab_groups(),
|
||||||
split_layouts: self.visible_split_layouts(),
|
split_layouts: self.visible_split_layouts(),
|
||||||
installed_plugins: self.installed_plugins.clone(),
|
installed_plugins: self.installed_plugins.clone(),
|
||||||
|
|||||||
@@ -2,6 +2,79 @@ use ely_domain::{DiagnosticsReportingPolicy, HistoryRecordingPolicy};
|
|||||||
|
|
||||||
use super::BrowserCore;
|
use super::BrowserCore;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct LocalDataInventory {
|
||||||
|
open_tabs: usize,
|
||||||
|
archived_tabs: usize,
|
||||||
|
bookmarks: usize,
|
||||||
|
notes: usize,
|
||||||
|
reading_list: usize,
|
||||||
|
history_entries: usize,
|
||||||
|
site_permissions: usize,
|
||||||
|
site_permission_audit_events: usize,
|
||||||
|
downloads: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalDataInventory {
|
||||||
|
#[must_use]
|
||||||
|
pub fn open_tabs(self) -> usize {
|
||||||
|
self.open_tabs
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn archived_tabs(self) -> usize {
|
||||||
|
self.archived_tabs
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn bookmarks(self) -> usize {
|
||||||
|
self.bookmarks
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn notes(self) -> usize {
|
||||||
|
self.notes
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn reading_list(self) -> usize {
|
||||||
|
self.reading_list
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn history_entries(self) -> usize {
|
||||||
|
self.history_entries
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn site_permissions(self) -> usize {
|
||||||
|
self.site_permissions
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn site_permission_audit_events(self) -> usize {
|
||||||
|
self.site_permission_audit_events
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn downloads(self) -> usize {
|
||||||
|
self.downloads
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn total_items(self) -> usize {
|
||||||
|
self.open_tabs
|
||||||
|
+ self.archived_tabs
|
||||||
|
+ self.bookmarks
|
||||||
|
+ self.notes
|
||||||
|
+ self.reading_list
|
||||||
|
+ self.history_entries
|
||||||
|
+ self.site_permissions
|
||||||
|
+ self.site_permission_audit_events
|
||||||
|
+ self.downloads
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BrowserCore {
|
impl BrowserCore {
|
||||||
pub fn set_history_recording_policy(&mut self, policy: HistoryRecordingPolicy) {
|
pub fn set_history_recording_policy(&mut self, policy: HistoryRecordingPolicy) {
|
||||||
self.history_recording_policy = policy;
|
self.history_recording_policy = policy;
|
||||||
@@ -25,4 +98,49 @@ impl BrowserCore {
|
|||||||
pub fn diagnostics_reporting_policy(&self) -> DiagnosticsReportingPolicy {
|
pub fn diagnostics_reporting_policy(&self) -> DiagnosticsReportingPolicy {
|
||||||
self.diagnostics_reporting_policy
|
self.diagnostics_reporting_policy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn active_profile_local_data_inventory(&self) -> LocalDataInventory {
|
||||||
|
let profile_id = &self.active_profile_id;
|
||||||
|
|
||||||
|
LocalDataInventory {
|
||||||
|
open_tabs: self.tabs.iter().filter(|tab| tab.profile_id() == profile_id).count(),
|
||||||
|
archived_tabs: self
|
||||||
|
.archived_tabs
|
||||||
|
.iter()
|
||||||
|
.filter(|archived| archived.tab().profile_id() == profile_id)
|
||||||
|
.count(),
|
||||||
|
bookmarks: self
|
||||||
|
.bookmarks
|
||||||
|
.iter()
|
||||||
|
.filter(|entry| entry.profile_id() == profile_id)
|
||||||
|
.count(),
|
||||||
|
notes: self.notes.iter().filter(|entry| entry.profile_id() == profile_id).count(),
|
||||||
|
reading_list: self
|
||||||
|
.reading_list
|
||||||
|
.iter()
|
||||||
|
.filter(|entry| entry.profile_id() == profile_id)
|
||||||
|
.count(),
|
||||||
|
history_entries: self
|
||||||
|
.history_entries
|
||||||
|
.iter()
|
||||||
|
.filter(|entry| entry.profile_id() == profile_id)
|
||||||
|
.count(),
|
||||||
|
site_permissions: self
|
||||||
|
.site_permissions
|
||||||
|
.iter()
|
||||||
|
.filter(|entry| entry.profile_id() == profile_id)
|
||||||
|
.count(),
|
||||||
|
site_permission_audit_events: self
|
||||||
|
.site_permission_audit_events
|
||||||
|
.iter()
|
||||||
|
.filter(|event| event.profile_id() == profile_id)
|
||||||
|
.count(),
|
||||||
|
downloads: self
|
||||||
|
.download_entries
|
||||||
|
.iter()
|
||||||
|
.filter(|entry| entry.profile_id() == profile_id)
|
||||||
|
.count(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use ely_browser_core::{BrowserCore, InitialBrowserConfig};
|
||||||
|
use ely_domain::{ProfileKind, SiteOrigin, SitePermissionDecision, SitePermissionFeature, UrlText};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn local_data_inventory_counts_active_profile_data() -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||||
|
let default_profile_id = core.snapshot()?.active_profile_id;
|
||||||
|
|
||||||
|
core.open_tab(UrlText::parse("https://example.com/research")?);
|
||||||
|
core.bookmark_active_tab()?;
|
||||||
|
core.save_active_url_note("profile note")?;
|
||||||
|
core.save_active_tab_to_reading_list()?;
|
||||||
|
core.record_download_started(
|
||||||
|
UrlText::parse("https://example.com/report.pdf")?,
|
||||||
|
"report.pdf",
|
||||||
|
Some(2048),
|
||||||
|
)?;
|
||||||
|
core.set_site_permission(
|
||||||
|
SiteOrigin::parse("https://example.com")?,
|
||||||
|
SitePermissionFeature::Camera,
|
||||||
|
SitePermissionDecision::AllowAlways,
|
||||||
|
)?;
|
||||||
|
let archived_tab_id = core.open_tab(UrlText::parse("https://servo.org/")?);
|
||||||
|
core.close_tab(&archived_tab_id)?;
|
||||||
|
|
||||||
|
let personal_profile_id = core.create_profile("Personal", 0xf54e00, ProfileKind::Standard)?;
|
||||||
|
core.open_tab(UrlText::parse("https://personal.example/research")?);
|
||||||
|
core.bookmark_active_tab()?;
|
||||||
|
core.save_active_url_note("personal note")?;
|
||||||
|
core.record_download_started(
|
||||||
|
UrlText::parse("https://personal.example/report.pdf")?,
|
||||||
|
"personal-report.pdf",
|
||||||
|
Some(4096),
|
||||||
|
)?;
|
||||||
|
core.set_site_permission(
|
||||||
|
SiteOrigin::parse("https://personal.example")?,
|
||||||
|
SitePermissionFeature::Notifications,
|
||||||
|
SitePermissionDecision::DenyAlways,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
core.select_profile(&default_profile_id)?;
|
||||||
|
let inventory = core.snapshot()?.local_data_inventory;
|
||||||
|
|
||||||
|
assert_eq!(inventory.open_tabs(), 2);
|
||||||
|
assert_eq!(inventory.archived_tabs(), 1);
|
||||||
|
assert_eq!(inventory.bookmarks(), 1);
|
||||||
|
assert_eq!(inventory.notes(), 1);
|
||||||
|
assert_eq!(inventory.reading_list(), 1);
|
||||||
|
assert_eq!(inventory.history_entries(), 2);
|
||||||
|
assert_eq!(inventory.site_permissions(), 1);
|
||||||
|
assert_eq!(inventory.site_permission_audit_events(), 1);
|
||||||
|
assert_eq!(inventory.downloads(), 1);
|
||||||
|
assert_eq!(inventory.total_items(), 11);
|
||||||
|
|
||||||
|
core.select_profile(&personal_profile_id)?;
|
||||||
|
let personal_inventory = core.snapshot()?.local_data_inventory;
|
||||||
|
|
||||||
|
assert_eq!(personal_inventory.open_tabs(), 2);
|
||||||
|
assert_eq!(personal_inventory.bookmarks(), 1);
|
||||||
|
assert_eq!(personal_inventory.notes(), 1);
|
||||||
|
assert_eq!(personal_inventory.history_entries(), 1);
|
||||||
|
assert_eq!(personal_inventory.site_permissions(), 1);
|
||||||
|
assert_eq!(personal_inventory.site_permission_audit_events(), 1);
|
||||||
|
assert_eq!(personal_inventory.downloads(), 1);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user