diff --git a/napture/src/b9/html.rs b/napture/src/b9/html.rs index e40b2300..a085c0c4 100644 --- a/napture/src/b9/html.rs +++ b/napture/src/b9/html.rs @@ -1,6 +1,6 @@ extern crate html_parser; -use crate::{lualog, Tab, globals::LUA_TIMEOUTS}; +use crate::{globals::LUA_TIMEOUTS, lualog, Tab}; use super::{ css::{self, Styleable}, @@ -26,7 +26,7 @@ fn decode_html_entities>(s: T) -> String { decode_html_entities(s.as_ref()).to_string() } -async fn parse_html(mut url: String) -> Result<(Node, Node)> { +async fn parse_html(mut url: String) -> Result<(Node, Node, String)> { let mut is_html = true; let mut file_name = String::new(); @@ -79,7 +79,7 @@ async fn parse_html(mut url: String) -> Result<(Node, Node)> { } }; - Ok((head, body)) + Ok((head, body, html)) } fn find_element_by_name(elements: &Vec, name: &str) -> Option { @@ -128,7 +128,7 @@ pub async fn build_ui( let mut css: String = css::reset_css(); - let (head, body) = match parse_html(furl.to_string()).await { + let (head, body, source_html) = match parse_html(furl.to_string()).await { Ok(ok) => ok, Err(e) => { eprintln!("Couldn't parse HTML: {}", e); @@ -136,6 +136,12 @@ pub async fn build_ui( } }; + // add html to source here after parse_html() + { + let mut page_source = tab.page_source.borrow_mut(); + page_source.add_file("index.html".to_string(), source_html.to_string()); + } + let head_elements = match head.element() { Some(ok) => ok, None => { @@ -166,6 +172,7 @@ pub async fn build_ui( } } + //css is pushed to css variable inside render_head() css.push_str(&html_view.style()); for element in body_elements.children.iter() { @@ -210,12 +217,22 @@ pub async fn build_ui( let tagss = Rc::clone(&tags); if !src.is_empty() { - let luacode = if src.starts_with("https://") { - fetch_file(src).await + + let src_clone = src.clone(); + + let luacode = if src_clone.starts_with("https://") { + fetch_file(src_clone).await } else { - fetch_file(format!("{}/{}", furl, src)).await + fetch_file(format!("{}/{}", furl, src_clone)).await }; + //add lua code to page source + { + let src_clone = src.clone(); + let mut page_source = tab.page_source.borrow_mut(); + page_source.add_file(src_clone.to_string(), luacode.to_string()); + } + if let Err(e) = super::lua::run(luacode, tags, tab.url.clone()).await { println!("ERROR: Failed to run lua: {}", e); } @@ -274,6 +291,11 @@ async fn render_head(element: &Element, contents: Option<&Node>, tab: Rc>, // id: String, } @@ -149,6 +152,13 @@ fn handle_search_update( tab_in_closure.url = dns_url; } + { + //clear page_source info for tab before fetching new page files + //enclosed to let borrowed mut go out of scope + let mut page_source_in_closure = tab_in_closure.page_source.borrow_mut(); + page_source_in_closure.clear(); + } + searchbar_mut.set_text(&url.replace("buss://", "")); searchbar_mut.set_position(-1); @@ -186,6 +196,7 @@ fn get_time() -> String { fn build_ui(app: &adw::Application, args: Rc>>, config: Rc>) { let history = Rc::new(RefCell::new(History::new())); + let page_source = Rc::new(RefCell::new(PageSource::new())); let default_url = if let Some(dev_build) = args.borrow().get(1) { // cli dev_build.to_string() @@ -221,7 +232,8 @@ fn build_ui(app: &adw::Application, args: Rc>>, config: Rc>>, config: Rc> = Rc::new(RefCell::new(app.clone())); let event_controller = gtk::EventControllerKey::new(); let history_ = Rc::clone(&history); + let page_source_ = Rc::clone(&page_source); + + // let lua_logs_showing = Rc::new(RefCell::new(false)); + // let settings_showing = Rc::new(RefCell::new(false)); + // let history_showing = Rc::new(RefCell::new(false)); + + let source_viewer_showing = Rc::new(RefCell::new(false)); + let source_viewer_window: Rc>> = Rc::new(RefCell::new(None)); + + let source_viewer_showing_ = source_viewer_showing.clone(); + let source_viewer_window_ = source_viewer_window.clone(); event_controller.connect_key_pressed(move |_, key, _a, b| { let app_clone = Rc::clone(&app_); let history_clone = Rc::clone(&history_); + let page_source_clone = Rc::clone(&page_source_); + + let source_viewer_showing_clone = source_viewer_showing_.clone(); + let mut source_viewer_window_ref = source_viewer_window_.borrow_mut(); if b == (gdk::ModifierType::SHIFT_MASK | gdk::ModifierType::CONTROL_MASK) && key == gdk::Key::P @@ -284,6 +311,17 @@ fn build_ui(app: &adw::Application, args: Rc>>, config: Rc>>, config: Rc>>, config: Rc> = Rc::new(RefCell::new(app.clone())); search.connect_activate({ + let rc_scroll_search = rc_scroll.clone(); + let rc_css_provider_search = rc_css_provider.clone(); + let rc_tab_search = rc_tab.clone(); let history = history.clone(); let go_forward = go_forward.clone(); let go_back = go_back.clone(); + let source_viewer_window_ = source_viewer_window.clone(); + let source_viewer_showing_= source_viewer_showing.clone(); + let page_source_ = page_source.clone(); + move |query| { update_buttons(&go_back, &go_forward, &history); handle_search_update( @@ -348,18 +394,33 @@ fn build_ui(app: &adw::Application, args: Rc>>, config: Rc> = Rc::new(RefCell::new(app.clone())); refresh_button.connect_clicked({ + let rc_scroll_refresh = rc_scroll.clone(); + let rc_css_provider_refresh = rc_css_provider.clone(); + let rc_tab_refresh = rc_tab.clone(); + let rc_search_refresh = rc_search.clone(); let history = history.clone(); + let source_viewer_window_ = source_viewer_window.clone(); + let source_viewer_showing_= source_viewer_showing.clone(); + let page_source_ = page_source.clone(); history .borrow_mut() .add_to_history(rc_search_refresh.borrow().text().to_string(), get_time(), true); @@ -370,17 +431,32 @@ fn build_ui(app: &adw::Application, args: Rc>>, config: Rc> = Rc::new(RefCell::new(app.clone())); home_button.connect_clicked({ - let history = history.clone(); + let rc_scroll_home = rc_scroll.clone(); + let rc_css_provider_home = rc_css_provider.clone(); + let rc_tab_home = rc_tab.clone(); + let rc_search_home = rc_search.clone(); + let history = history.clone(); let go_forward = go_forward.clone(); let go_back = go_back.clone(); + let source_viewer_window_ = source_viewer_window.clone(); + let source_viewer_showing_= source_viewer_showing.clone(); + let page_source_ = page_source.clone(); move |_button| { rc_search_home.borrow_mut().set_text(DEFAULT_URL); history @@ -393,17 +469,32 @@ fn build_ui(app: &adw::Application, args: Rc>>, config: Rc> = Rc::new(RefCell::new(app.clone())); go_back.connect_clicked({ + let rc_scroll_back = rc_scroll.clone(); + let rc_css_provider_back = rc_css_provider.clone(); + let rc_tab_back = rc_tab.clone(); + let rc_search_back = rc_search.clone(); let history = history.clone(); let go_forward = go_forward.clone(); let go_back = go_back.clone(); + let source_viewer_window_ = source_viewer_window.clone(); + let source_viewer_showing_= source_viewer_showing.clone(); + let page_source_ = page_source.clone(); move |_| { history.borrow_mut().go_back(); update_buttons(&go_back, &go_forward, &history); @@ -415,30 +506,153 @@ fn build_ui(app: &adw::Application, args: Rc>>, config: Rc> = Rc::new(RefCell::new(app.clone())); go_forward.connect_clicked({ + let rc_scroll_forward = rc_scroll.clone(); + let rc_css_provider_forward = rc_css_provider.clone(); + let rc_tab_forward = rc_tab.clone(); + let rc_search_forward = rc_search.clone(); let history = history.clone(); let go_forward = go_forward.clone(); let go_back = go_back.clone(); + let source_viewer_window_ = source_viewer_window.clone(); + let source_viewer_showing_= source_viewer_showing.clone(); + let page_source_ = page_source.clone(); move |_| { history.borrow_mut().go_forward(); update_buttons(&go_back, &go_forward, &history); let current_url = history.borrow().current().unwrap().url.clone(); - rc_search.borrow_mut().set_text(¤t_url); + rc_search_forward.borrow_mut().set_text(¤t_url); handle_search_update( rc_scroll_forward.clone(), rc_css_provider_forward.clone(), - rc_tab.clone(), - rc_search.clone(), + rc_tab_forward.clone(), + rc_search_forward.clone(), ); + //If open, refresh source viewer on new page + if *source_viewer_showing_.clone().borrow_mut() { + let mut source_viewer_window_ref = source_viewer_window_.borrow_mut(); + let page_source_clone = Rc::clone(&page_source_); + let app_clone = app_.clone(); + + if let Some(ref window) = *source_viewer_window_ref{ + window.close(); + *source_viewer_window_ref = Some(display_source_viewer(&app_clone, page_source_clone, source_viewer_showing_.clone())); + } + } } }); - glib::source::timeout_add_local(std::time::Duration::from_millis(5000), move || { // every 5 seconds remove "stale" timeouts + //mouse button listeners for forward/back page navigation + //mouse back - button "8" + let gesture_back = gtk::GestureClick::new(); + gesture_back.set_button(8); + + let app_: Rc> = Rc::new(RefCell::new(app.clone())); + gesture_back.connect_pressed({ + let rc_scroll_back = rc_scroll.clone(); + let rc_css_provider_back = rc_css_provider.clone(); + let rc_tab_back = rc_tab.clone(); + let rc_search_back = rc_search.clone(); + let history = history.clone(); + let go_forward = go_forward.clone(); + let go_back = go_back.clone(); + let source_viewer_window_ = source_viewer_window.clone(); + let source_viewer_showing_= source_viewer_showing.clone(); + let page_source_ = page_source.clone(); + move |gesture_back, n, _x, _y| { + gesture_back.set_state(gtk::EventSequenceState::Claimed); + + if n < 2 { + history.borrow_mut().go_back(); + update_buttons(&go_back, &go_forward, &history); + let current_url = history.borrow().current().unwrap().url.clone(); + rc_search_back.borrow_mut().set_text(¤t_url); + handle_search_update( + rc_scroll_back.clone(), + rc_css_provider_back.clone(), + rc_tab_back.clone(), + rc_search_back.clone(), + ); + //If open, refresh source viewer on new page + if *source_viewer_showing_.clone().borrow_mut() { + let mut source_viewer_window_ref = source_viewer_window_.borrow_mut(); + let page_source_clone = Rc::clone(&page_source_); + let app_clone = app_.clone(); + + if let Some(ref window) = *source_viewer_window_ref{ + window.close(); + *source_viewer_window_ref = Some(display_source_viewer(&app_clone, page_source_clone, source_viewer_showing_.clone())); + } + } + } + }}); + + window.add_controller(gesture_back); + + //mouse forward - button "9" + let gesture_forward = gtk::GestureClick::new(); + gesture_forward.set_button(9); + + let app_: Rc> = Rc::new(RefCell::new(app.clone())); + gesture_forward.connect_pressed({ + let rc_scroll_forward = rc_scroll.clone(); + let rc_css_provider_forward = rc_css_provider.clone(); + let rc_tab_forward = rc_tab.clone(); + let rc_search_forward = rc_search.clone(); + let history = history.clone(); + let go_forward = go_forward.clone(); + let go_back = go_back.clone(); + let source_viewer_window_ = source_viewer_window.clone(); + let source_viewer_showing_= source_viewer_showing.clone(); + let page_source_ = page_source.clone(); + move |gesture_forward, n, _x, _y|{ + + gesture_forward.set_state(gtk::EventSequenceState::Claimed); + + if n < 2 { + history.borrow_mut().go_forward(); + update_buttons(&go_back, &go_forward, &history); + let current_url = history.borrow().current().unwrap().url.clone(); + rc_search_forward.borrow_mut().set_text(¤t_url); + handle_search_update( + rc_scroll_forward.clone(), + rc_css_provider_forward.clone(), + rc_tab_forward.clone(), + rc_search_forward.clone() + ); + //If open, refresh source viewer on new page + if *source_viewer_showing_.clone().borrow_mut() { + let mut source_viewer_window_ref = source_viewer_window_.borrow_mut(); + let page_source_clone = Rc::clone(&page_source_); + let app_clone = app_.clone(); + + if let Some(ref window) = *source_viewer_window_ref{ + window.close(); + *source_viewer_window_ref = Some(display_source_viewer(&app_clone, page_source_clone, source_viewer_showing_.clone())); + } + } + } + }}); + + window.add_controller(gesture_forward); + + // every 5 seconds remove "stale" timeouts + glib::source::timeout_add_local(std::time::Duration::from_millis(5000), move || { let mut timeouts = LUA_TIMEOUTS.lock().unwrap(); timeouts.retain(|source| { !source.is_destroyed() @@ -447,6 +661,10 @@ fn build_ui(app: &adw::Application, args: Rc>>, config: Rc>, css_provider: Rc>){ + +// } + // commented code here was an attempt at implementing multiple tabs. // it will be kept here in case I decide to implement multiple tabs again fn make_tab( @@ -456,6 +674,7 @@ fn make_tab( // cursor_pointer: Option<&Cursor>, mut tabs: Vec, default_url: String, + page_source: Rc>, ) -> Tab { // let tabid = gen_tab_id(); @@ -517,6 +736,7 @@ fn make_tab( // id: tabid, label_widget: tabname, icon_widget: tabicon, + page_source: page_source }; tabs.push(res.clone()); @@ -611,7 +831,16 @@ fn fetch_dns(url: String) -> String { if let Ok(json) = response.json::() { let path = url.split_once('/') .unwrap_or(("", "")).1; - json.ip + &format!("/{}", path) + + //check if json.ip is a standalone IP address, + //if so, prepend "http://" to make it parsable + //as a url. + let ip = match json.ip.parse::() { + Ok(_ip) => format!("http://{}", json.ip), + Err(_) => json.ip.clone(), + }; + + ip + &format!("/{}", path) } else { lualog!( "debug", @@ -628,6 +857,96 @@ fn fetch_dns(url: String) -> String { } } +fn display_source_viewer(app: &Rc>, page_source: Rc>, showing: Rc>) -> Window{ + + let window: Window = Object::builder() + .property("application", glib::Value::from(&*app.borrow_mut())) + .build(); + + window.set_default_size(800, 900); + window.set_height_request(500); + window.set_width_request(800); + + // Create the main container box with horizontal orientation + let main_box = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .spacing(6) + .margin_top(6) + .margin_bottom(6) + .margin_start(6) + .margin_end(6) + .build(); + + // Create the menu box for the left side + let menu_box = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) + .spacing(6) + .width_request(140) + .build(); + + // scroller for text area + let content_scroll = gtk::ScrolledWindow::builder().build(); + + let content_text_view = gtk::TextView::builder() + .wrap_mode(gtk::WrapMode::Word) + .editable(false) + .hexpand(true) + .build(); + + content_scroll.set_child(Some(&content_text_view)); + + // let files = page_source.get_files(); + + // create buffers and listeners to update the text area buffer + let content_buffer = content_text_view.buffer(); + + for file in page_source.borrow().get_files() { + + let button = gtk::Button::with_label(file.get(0).expect("")); + + let file_clone = file.clone(); + + button.connect_clicked({ + let content_buffer = content_buffer.clone(); + + move |_| { + content_buffer.set_text(file_clone.get(1).expect("")); + } + }); + + menu_box.append(&button); + } + + // Add menu box and content area to the main box + main_box.append(&menu_box); + main_box.append(&content_scroll); + + window.set_child(Some(&main_box)); + + let label = gtk::Label::new(Some("Source")); + let empty_label = gtk::Label::new(Some("")); + let headerbar = gtk::HeaderBar::builder().build(); + + headerbar.pack_start(&label); + headerbar.set_title_widget(Some(&empty_label)); + + window.set_titlebar(Some(&headerbar)); + + let showing_clone = showing.clone(); + window.connect_close_request(move |_|{ + + *showing_clone.borrow_mut() = false; + + gtk::glib::Propagation::Proceed + }); + + window.present(); + + *showing.borrow_mut() = true; + + window +} + fn display_lua_logs(app: &Rc>) { let window: Window = Object::builder() .property("application", glib::Value::from(&*app.borrow_mut())) diff --git a/napture/src/page_source_mod/mod.rs b/napture/src/page_source_mod/mod.rs new file mode 100644 index 00000000..e7d37124 --- /dev/null +++ b/napture/src/page_source_mod/mod.rs @@ -0,0 +1,25 @@ +#[derive(Debug)] +pub struct PageSource { + files: Vec>, +} + +impl PageSource { + pub fn new() -> Self { + PageSource { + files: Vec::new(), + } + } + + pub fn add_file(&mut self, name: String, contents: String){ + let file = vec![name, contents]; + self.files.push(file); + } + + pub fn get_files(&self) -> &Vec>{ + &self.files + } + + pub fn clear(&mut self){ + self.files.clear(); + } +} \ No newline at end of file