fix: browser/commands.rs - remove history::record_visit, vault_detector_script, autofill_suppress_script references
Browse files
src-tauri/src/browser/commands.rs
CHANGED
|
@@ -126,17 +126,11 @@ pub async fn tab_find(app: AppHandle, tab_id: String, query: String) -> Result<u
|
|
| 126 |
#[tauri::command]
|
| 127 |
pub async fn tab_find_clear(app: AppHandle, tab_id: String) -> Result<(), String> { eval_on_tab(&app, &tab_id, "window.__muse_find_cleanup && window.__muse_find_cleanup()") }
|
| 128 |
|
| 129 |
-
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 130 |
-
// INTERNAL: Child webview creation
|
| 131 |
-
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 132 |
-
|
| 133 |
pub(crate) async fn create_tab_inner(app: &AppHandle, url: &str, layout: &ViewportLayout, show_immediately: bool) -> Result<String, String> {
|
| 134 |
let id_num = app.state::<AppState>().next_tab_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
| 135 |
let id = format!("tab-{id_num}");
|
| 136 |
let label = format!("muse-tab-{id_num}");
|
| 137 |
let parsed = Url::parse(url).map_err(|e| e.to_string())?;
|
| 138 |
-
|
| 139 |
-
// Build the full initialization script (adblock + privacy + hover overlay + vault detector + autofill suppression)
|
| 140 |
let init_script = scripts::build_init_script(&scripts::blocked_domains_json());
|
| 141 |
let full_init = format!("{init_script}\n{CONTEXT_MENU_BLOCK_JS}\n{FAVICON_SCRIPT}");
|
| 142 |
|
|
@@ -147,25 +141,14 @@ pub(crate) async fn create_tab_inner(app: &AppHandle, url: &str, layout: &Viewpo
|
|
| 147 |
let app_for_cosmetic = app.clone();
|
| 148 |
|
| 149 |
let builder = WebviewBuilder::new(label.clone(), WebviewUrl::External(parsed))
|
| 150 |
-
// Inject all scripts before any page JS runs
|
| 151 |
.initialization_script(&full_init)
|
| 152 |
-
// Disable native WebView autofill (Windows WebView2: general autofill suggestions)
|
| 153 |
-
// macOS/Linux: this is a noop β autofill suppression handled by JS injection instead
|
| 154 |
-
.general_autofill_enabled(false)
|
| 155 |
-
// Page load handler: re-inject scripts, apply adblock cosmetics, record history
|
| 156 |
.on_page_load(move |webview, payload| {
|
| 157 |
let url = payload.url().to_string();
|
| 158 |
let loading = matches!(payload.event(), PageLoadEvent::Started);
|
| 159 |
update_tab_field(&app_for_load, &id_for_load, |t| { t.url = url.clone(); t.loading = loading; });
|
| 160 |
-
|
| 161 |
if matches!(payload.event(), PageLoadEvent::Finished) {
|
| 162 |
-
// Re-inject scripts that may not persist across cross-origin navigations
|
| 163 |
let _ = webview.eval(CONTEXT_MENU_BLOCK_JS);
|
| 164 |
let _ = webview.eval(scripts::hover_overlay_script());
|
| 165 |
-
let _ = webview.eval(scripts::vault_detector_script());
|
| 166 |
-
let _ = webview.eval(scripts::autofill_suppress_script());
|
| 167 |
-
|
| 168 |
-
// Adblock cosmetic filtering + scriptlets
|
| 169 |
let adblock_state = app_for_cosmetic.state::<AdBlockState>();
|
| 170 |
let css = adblock_state.get_cosmetic_css(&url);
|
| 171 |
if !css.is_empty() {
|
|
@@ -174,53 +157,31 @@ pub(crate) async fn create_tab_inner(app: &AppHandle, url: &str, layout: &Viewpo
|
|
| 174 |
}
|
| 175 |
let scriptlet_js = adblock_state.get_injected_script(&url);
|
| 176 |
if !scriptlet_js.is_empty() { let _ = webview.eval(&format!("try{{{scriptlet_js}}}catch(e){{}}")); }
|
| 177 |
-
|
| 178 |
-
// Favicon extraction
|
| 179 |
let _ = webview.eval("window.__muse_report_favicon && window.__muse_report_favicon()");
|
| 180 |
-
|
| 181 |
-
// Record page visit in app-managed history
|
| 182 |
-
let title_for_history = {
|
| 183 |
-
let state = app_for_load.state::<AppState>();
|
| 184 |
-
state.tabs.lock().ok().and_then(|tabs| tabs.tabs.get(&id_for_load).map(|t| t.title.clone())).unwrap_or_default()
|
| 185 |
-
};
|
| 186 |
-
let _ = crate::history::record_visit(&app_for_load, id_for_load.clone(), url.clone(), title_for_history);
|
| 187 |
}
|
| 188 |
})
|
| 189 |
-
|
| 190 |
-
.on_document_title_changed(move |_webview, title| {
|
| 191 |
-
update_tab_field(&app_for_title, &id_for_title, |t| { if !title.trim().is_empty() { t.title = title.clone(); } });
|
| 192 |
-
})
|
| 193 |
-
// Navigation handler: intercept muse-action:// beacons, block ad domains
|
| 194 |
.on_navigation({ let app_nav = app.clone(); move |url| {
|
| 195 |
let s = url.as_str();
|
| 196 |
if s.starts_with("muse-action://") { handle_muse_action(&app_nav, s); return false; }
|
| 197 |
!navigation_blocked(&app_nav, s)
|
| 198 |
}});
|
| 199 |
|
| 200 |
-
// Add child webview to the main window
|
| 201 |
let window = app.get_window("main").ok_or("main window not found")?;
|
| 202 |
let (cx, cy, cw, ch) = if show_immediately && layout.width > 10.0 { bounds(layout) } else { (-32000.0, -32000.0, 1.0, 1.0) };
|
| 203 |
window.add_child(builder, LogicalPosition::new(cx, cy), LogicalSize::new(cw, ch)).map_err(|e| e.to_string())?;
|
| 204 |
if !show_immediately { if let Some(webview) = app.get_webview(&label) { let _ = webview.hide(); } }
|
| 205 |
|
| 206 |
-
// Apply per-domain zoom memory
|
| 207 |
let domain = extract_domain(url);
|
| 208 |
let saved_zoom = { let state = app.state::<AppState>(); let tabs = state.tabs.lock().map_err(|_| "lock")?; tabs.get_zoom_for_domain(&domain) };
|
| 209 |
let now = chrono::Utc::now().timestamp();
|
| 210 |
let zoom = saved_zoom.unwrap_or(1.0);
|
| 211 |
-
|
| 212 |
-
// Register tab in state and activate
|
| 213 |
let previous = { let state = app.state::<AppState>(); let mut tabs = state.tabs.lock().map_err(|_| "lock")?; let previous = tabs.active.clone(); tabs.active = Some(id.clone()); tabs.order.push(id.clone()); tabs.tabs.insert(id.clone(), BrowserTab { id: id.clone(), label: label.clone(), url: url.to_string(), title: "New Tab".to_string(), favicon: None, loading: true, pinned: false, sleeping: false, zoom, can_go_back: false, can_go_forward: false, last_active: now }); previous };
|
| 214 |
if zoom != 1.0 { if let Some(webview) = app.get_webview(&label) { let _ = webview.set_zoom(zoom); } }
|
| 215 |
if let Some(prev) = previous { if prev != id { hide_tab(app, &prev)?; } }
|
| 216 |
-
emit_snapshot(app)?;
|
| 217 |
-
Ok(id)
|
| 218 |
}
|
| 219 |
|
| 220 |
-
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 221 |
-
// muse-action:// beacon handler (hover overlay, vault detector)
|
| 222 |
-
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 223 |
-
|
| 224 |
fn handle_muse_action(app: &AppHandle, raw: &str) {
|
| 225 |
let rest = raw.trim_start_matches("muse-action://");
|
| 226 |
let (_action, query) = rest.split_once('?').unwrap_or((rest, ""));
|
|
@@ -229,28 +190,20 @@ fn handle_muse_action(app: &AppHandle, raw: &str) {
|
|
| 229 |
if url.is_empty() { return; }
|
| 230 |
let title = params.get("title").cloned();
|
| 231 |
let source = params.get("source").cloned();
|
| 232 |
-
|
| 233 |
let app2 = app.clone();
|
| 234 |
tauri::async_runtime::spawn(async move {
|
| 235 |
match crate::library::library_add_item(app2.clone(), url.clone(), source, title).await {
|
| 236 |
Ok(item) => {
|
| 237 |
-
let _ = crate::board::board_add_image(app2.clone(), Some(item.id.clone()), item.data_url.clone(),
|
| 238 |
-
let _ = app2.emit("board://image_added", serde_json::json!({"id": item.id
|
| 239 |
}
|
| 240 |
-
Err(e) => eprintln!("[muse-action]
|
| 241 |
}
|
| 242 |
});
|
| 243 |
}
|
| 244 |
|
| 245 |
-
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 246 |
-
// Utility functions
|
| 247 |
-
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 248 |
-
|
| 249 |
fn parse_query(query: &str) -> HashMap<String, String> {
|
| 250 |
-
query.split('&').filter_map(|pair| {
|
| 251 |
-
let (k, v) = pair.split_once('=')?;
|
| 252 |
-
Some((percent_decode(k), percent_decode(v)))
|
| 253 |
-
}).collect()
|
| 254 |
}
|
| 255 |
|
| 256 |
fn percent_decode(s: &str) -> String {
|
|
@@ -259,7 +212,7 @@ fn percent_decode(s: &str) -> String {
|
|
| 259 |
let mut i = 0;
|
| 260 |
while i < bytes.len() {
|
| 261 |
if bytes[i] == b'%' && i + 2 < bytes.len() {
|
| 262 |
-
if let Ok(hex) = std::str::from_utf8(&bytes[i
|
| 263 |
if let Ok(v) = u8::from_str_radix(hex, 16) { out.push(v); i += 3; continue; }
|
| 264 |
}
|
| 265 |
}
|
|
|
|
| 126 |
#[tauri::command]
|
| 127 |
pub async fn tab_find_clear(app: AppHandle, tab_id: String) -> Result<(), String> { eval_on_tab(&app, &tab_id, "window.__muse_find_cleanup && window.__muse_find_cleanup()") }
|
| 128 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
pub(crate) async fn create_tab_inner(app: &AppHandle, url: &str, layout: &ViewportLayout, show_immediately: bool) -> Result<String, String> {
|
| 130 |
let id_num = app.state::<AppState>().next_tab_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
| 131 |
let id = format!("tab-{id_num}");
|
| 132 |
let label = format!("muse-tab-{id_num}");
|
| 133 |
let parsed = Url::parse(url).map_err(|e| e.to_string())?;
|
|
|
|
|
|
|
| 134 |
let init_script = scripts::build_init_script(&scripts::blocked_domains_json());
|
| 135 |
let full_init = format!("{init_script}\n{CONTEXT_MENU_BLOCK_JS}\n{FAVICON_SCRIPT}");
|
| 136 |
|
|
|
|
| 141 |
let app_for_cosmetic = app.clone();
|
| 142 |
|
| 143 |
let builder = WebviewBuilder::new(label.clone(), WebviewUrl::External(parsed))
|
|
|
|
| 144 |
.initialization_script(&full_init)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
.on_page_load(move |webview, payload| {
|
| 146 |
let url = payload.url().to_string();
|
| 147 |
let loading = matches!(payload.event(), PageLoadEvent::Started);
|
| 148 |
update_tab_field(&app_for_load, &id_for_load, |t| { t.url = url.clone(); t.loading = loading; });
|
|
|
|
| 149 |
if matches!(payload.event(), PageLoadEvent::Finished) {
|
|
|
|
| 150 |
let _ = webview.eval(CONTEXT_MENU_BLOCK_JS);
|
| 151 |
let _ = webview.eval(scripts::hover_overlay_script());
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
let adblock_state = app_for_cosmetic.state::<AdBlockState>();
|
| 153 |
let css = adblock_state.get_cosmetic_css(&url);
|
| 154 |
if !css.is_empty() {
|
|
|
|
| 157 |
}
|
| 158 |
let scriptlet_js = adblock_state.get_injected_script(&url);
|
| 159 |
if !scriptlet_js.is_empty() { let _ = webview.eval(&format!("try{{{scriptlet_js}}}catch(e){{}}")); }
|
|
|
|
|
|
|
| 160 |
let _ = webview.eval("window.__muse_report_favicon && window.__muse_report_favicon()");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
}
|
| 162 |
})
|
| 163 |
+
.on_document_title_changed(move |_webview, title| { update_tab_field(&app_for_title, &id_for_title, |t| { if !title.trim().is_empty() { t.title = title.clone(); } }); })
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
.on_navigation({ let app_nav = app.clone(); move |url| {
|
| 165 |
let s = url.as_str();
|
| 166 |
if s.starts_with("muse-action://") { handle_muse_action(&app_nav, s); return false; }
|
| 167 |
!navigation_blocked(&app_nav, s)
|
| 168 |
}});
|
| 169 |
|
|
|
|
| 170 |
let window = app.get_window("main").ok_or("main window not found")?;
|
| 171 |
let (cx, cy, cw, ch) = if show_immediately && layout.width > 10.0 { bounds(layout) } else { (-32000.0, -32000.0, 1.0, 1.0) };
|
| 172 |
window.add_child(builder, LogicalPosition::new(cx, cy), LogicalSize::new(cw, ch)).map_err(|e| e.to_string())?;
|
| 173 |
if !show_immediately { if let Some(webview) = app.get_webview(&label) { let _ = webview.hide(); } }
|
| 174 |
|
|
|
|
| 175 |
let domain = extract_domain(url);
|
| 176 |
let saved_zoom = { let state = app.state::<AppState>(); let tabs = state.tabs.lock().map_err(|_| "lock")?; tabs.get_zoom_for_domain(&domain) };
|
| 177 |
let now = chrono::Utc::now().timestamp();
|
| 178 |
let zoom = saved_zoom.unwrap_or(1.0);
|
|
|
|
|
|
|
| 179 |
let previous = { let state = app.state::<AppState>(); let mut tabs = state.tabs.lock().map_err(|_| "lock")?; let previous = tabs.active.clone(); tabs.active = Some(id.clone()); tabs.order.push(id.clone()); tabs.tabs.insert(id.clone(), BrowserTab { id: id.clone(), label: label.clone(), url: url.to_string(), title: "New Tab".to_string(), favicon: None, loading: true, pinned: false, sleeping: false, zoom, can_go_back: false, can_go_forward: false, last_active: now }); previous };
|
| 180 |
if zoom != 1.0 { if let Some(webview) = app.get_webview(&label) { let _ = webview.set_zoom(zoom); } }
|
| 181 |
if let Some(prev) = previous { if prev != id { hide_tab(app, &prev)?; } }
|
| 182 |
+
emit_snapshot(app)?; Ok(id)
|
|
|
|
| 183 |
}
|
| 184 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
fn handle_muse_action(app: &AppHandle, raw: &str) {
|
| 186 |
let rest = raw.trim_start_matches("muse-action://");
|
| 187 |
let (_action, query) = rest.split_once('?').unwrap_or((rest, ""));
|
|
|
|
| 190 |
if url.is_empty() { return; }
|
| 191 |
let title = params.get("title").cloned();
|
| 192 |
let source = params.get("source").cloned();
|
|
|
|
| 193 |
let app2 = app.clone();
|
| 194 |
tauri::async_runtime::spawn(async move {
|
| 195 |
match crate::library::library_add_item(app2.clone(), url.clone(), source, title).await {
|
| 196 |
Ok(item) => {
|
| 197 |
+
let _ = crate::board::board_add_image(app2.clone(), Some(item.id.clone()), item.data_url.clone(), 120.0, 120.0, 300.0, 200.0);
|
| 198 |
+
let _ = app2.emit("board://image_added", serde_json::json!({"id": item.id}));
|
| 199 |
}
|
| 200 |
+
Err(e) => eprintln!("[muse-action] capture failed: {e}"),
|
| 201 |
}
|
| 202 |
});
|
| 203 |
}
|
| 204 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
fn parse_query(query: &str) -> HashMap<String, String> {
|
| 206 |
+
query.split('&').filter_map(|pair| { let (k, v) = pair.split_once('=')?; Some((percent_decode(k), percent_decode(v))) }).collect()
|
|
|
|
|
|
|
|
|
|
| 207 |
}
|
| 208 |
|
| 209 |
fn percent_decode(s: &str) -> String {
|
|
|
|
| 212 |
let mut i = 0;
|
| 213 |
while i < bytes.len() {
|
| 214 |
if bytes[i] == b'%' && i + 2 < bytes.len() {
|
| 215 |
+
if let Ok(hex) = std::str::from_utf8(&bytes[i+1..i+3]) {
|
| 216 |
if let Ok(v) = u8::from_str_radix(hex, 16) { out.push(v); i += 3; continue; }
|
| 217 |
}
|
| 218 |
}
|