host: plugins, custom CSS, feature flags, settings and open_panel are host commands
run_app now keeps and serves what every app's panel and page need, with the same caller checks
as the rest (commands::SHELL: shell_only first; testkit::assert_host_guards covers them):
- app.rs: PageState (the plugin store in app data `plugins`, the feature flags, ShellSettings)
managed by the host; commands plugins_list, plugin_set_enabled, plugins_open_folder,
custom_css_get/set, features_get/set, settings_get/set (moved from wayBetter for X). The app
reads the state through app::page(), gets a loaded document's scripts from app::page_scripts()
(flags, links, custom CSS, plugins) and writes through set_features/set_settings/set_custom_css
(a backup import). AppConfig takes the app's PluginHost (AppConfig::new(site, title, plugins))
and an optional home_url. SiteHooks::navigation_started and new_window_mode default to the
owner's link settings.
- settings.rs (pure, tested): ShellSettings { homeUrl, externalLinks, siteLinks, restoreTabs }
with its input form (missing fields take the defaults; `xLinks`, the name wayBetter for X used,
is read as siteLinks), links_script ({ site: mode }), feature validation and features_script.
- commands::SITE + open_panel: a tab on the site's origin asks the shell to open the app's panel
(site_tab_only first). testkit: assert_site_only(command, site) checks that every capability
granting a site command is a tab capability limited to the site's URLs; assert_host_commands and
assert_host_guards include the site commands.
- shell frame backupModel: a settings section that still says xLinks compares as siteLinks.
This commit is contained in:
parent
cecaf93639
commit
205ee82296
7 changed files with 529 additions and 36 deletions
|
|
@ -132,12 +132,31 @@ impl Capabilities {
|
|||
assert!(granting.is_empty(), "{permission} is granted by {granting:?}");
|
||||
}
|
||||
|
||||
/// The host's commands ([`commands::SHELL`], [`commands::TAB`]) keep their callers.
|
||||
/// `command` is granted, and only to tab webviews on `site`'s own pages: every capability
|
||||
/// granting it is a tab capability whose remote URLs are exactly `https://<host>/*` of the
|
||||
/// site's hosts.
|
||||
pub fn assert_site_only(&self, command: &str, site: &crate::site::SiteSpec) {
|
||||
self.assert_tab_only(command);
|
||||
let permission = commands::permission(command);
|
||||
let allowed: Vec<String> = site.hosts.iter().map(|host| format!("https://{host}/*")).collect();
|
||||
for cap in self.granting(&permission) {
|
||||
let urls: Vec<&str> = cap.json["remote"]["urls"].as_array().map(|list| list.iter().filter_map(Value::as_str).collect()).unwrap_or_default();
|
||||
assert!(
|
||||
!urls.is_empty() && urls.iter().all(|url| allowed.iter().any(|ok| ok == url)),
|
||||
"{permission} is for {allowed:?} only, but capability {:?} ({}) grants it on {urls:?}",
|
||||
cap.identifier(),
|
||||
cap.file
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The host's commands ([`commands::SHELL`], [`commands::TAB`], [`commands::SITE`]) keep their
|
||||
/// callers. Check the site commands' URLs with [`Self::assert_site_only`] too.
|
||||
pub fn assert_host_commands(&self) {
|
||||
for command in commands::SHELL {
|
||||
self.assert_shell_only(command);
|
||||
}
|
||||
for command in commands::TAB {
|
||||
for command in commands::TAB.iter().chain(commands::SITE) {
|
||||
self.assert_tab_only(command);
|
||||
}
|
||||
}
|
||||
|
|
@ -179,15 +198,23 @@ pub fn assert_host_guards() {
|
|||
for command in commands::TAB {
|
||||
assert_guarded(source, command, "tab_only(&webview)?");
|
||||
}
|
||||
for command in commands::SITE {
|
||||
assert_guarded(source, command, "site_tab_only(config(&app).site, &webview)?");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn caps(shell_extra: &str, tab_extra: &str) -> Capabilities {
|
||||
fn caps_with(shell_extra: &str, tab_extra: &str, site_extra: &str) -> Capabilities {
|
||||
let host_shell: Vec<String> = commands::SHELL.iter().map(|c| format!("\"{}\"", commands::permission(c))).collect();
|
||||
let host_tab: Vec<String> = commands::TAB.iter().map(|c| format!("\"{}\"", commands::permission(c))).collect();
|
||||
let host_site: Vec<String> = commands::SITE.iter().map(|c| format!("\"{}\"", commands::permission(c))).collect();
|
||||
let site = format!(
|
||||
r#"{{"identifier":"site-tab","webviews":["tab-*"],"local":false,"remote":{{"urls":["https://x.com/*"]}},"permissions":[{}{site_extra}]}}"#,
|
||||
host_site.join(",")
|
||||
);
|
||||
let shell = format!(
|
||||
r#"{{"identifier":"shell","webviews":["shell"],"local":true,"permissions":["core:event:default",{}{shell_extra}]}}"#,
|
||||
host_shell.join(",")
|
||||
|
|
@ -196,7 +223,11 @@ mod tests {
|
|||
r#"{{"identifier":"tab-chrome","webviews":["tab-*"],"local":false,"remote":{{"urls":["https://*/*"]}},"permissions":[{}{tab_extra}]}}"#,
|
||||
host_tab.join(",")
|
||||
);
|
||||
Capabilities::from_sources(&[("shell.json", &shell), ("tab-chrome.json", &tab)])
|
||||
Capabilities::from_sources(&[("shell.json", &shell), ("tab-chrome.json", &tab), ("site-tab.json", &site)])
|
||||
}
|
||||
|
||||
fn caps(shell_extra: &str, tab_extra: &str) -> Capabilities {
|
||||
caps_with(shell_extra, tab_extra, "")
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -219,6 +250,15 @@ mod tests {
|
|||
caps(r#","allow-tab-shortcut""#, "").assert_host_commands();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn site_commands_are_for_the_site_only() {
|
||||
use crate::site::testing::SITE;
|
||||
caps("", "").assert_site_only("open_panel", &SITE);
|
||||
// Granted to every tab (any site), a site command fails.
|
||||
let wide = std::panic::catch_unwind(|| caps("", r#","allow-open-panel""#).assert_site_only("open_panel", &SITE));
|
||||
assert!(wide.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "plugin API")]
|
||||
fn plugin_apis_are_refused() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue