diff --git a/Cargo.lock b/Cargo.lock index 2602ed5..27f4df2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -849,6 +849,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures" version = "0.3.30" @@ -1390,6 +1399,17 @@ dependencies = [ "iced_on_focus_widget", "smol_str", "ul-next", + "url", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", ] [[package]] @@ -2980,6 +3000,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-properties" version = "0.1.1" @@ -3010,6 +3039,17 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "version_check" version = "0.9.5" diff --git a/Cargo.toml b/Cargo.toml index fb84c80..c6af8f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ iced_on_focus_widget = "0.1.0" # reqwest = { version = "0.12.5", features = ["blocking"] } smol_str = "0.2.2" ul-next = { version = "0.2.0", optional = true } +url = "2.5.2" [features] default = ["webkit"] diff --git a/examples/basic_browser.rs b/examples/basic_browser.rs index 1814121..9c4fc1c 100644 --- a/examples/basic_browser.rs +++ b/examples/basic_browser.rs @@ -3,12 +3,11 @@ use iced::{executor, Command, Subscription}; use iced::{widget::column, Application, Settings, Theme}; use iced_aw::BOOTSTRAP_FONT_BYTES; - -use icy_browser::{browser_view, nav_bar, tab_bar, BrowserView, NavBar, State, TabBar, Ultralight}; - use std::borrow::Borrow; use std::time::Duration; +use icy_browser::{browser_view, nav_bar, tab_bar, BrowserView, NavBar, State, TabBar, Ultralight}; + fn main() -> Result<(), iced::Error> { let bootstrap_font = BOOTSTRAP_FONT_BYTES.into(); let settings = Settings { @@ -71,15 +70,9 @@ impl Application for Browser { fn update(&mut self, message: Self::Message) -> Command { match message { Message::DoWork => self.state.borrow().do_work(), - Message::NavBar(nav_bar_message) => match self.nav_bar.update(nav_bar_message) { - nav_bar::Action::None => {} - }, - Message::TabBar(tab_bar_message) => match self.tab_bar.update(tab_bar_message) { - tab_bar::Action::None => {} - }, - Message::BrowserView(browser_view) => match self.browser_view.update(browser_view) { - browser_view::Action::None => {} - }, + Message::NavBar(nav_bar_message) => self.nav_bar.update(nav_bar_message), + Message::TabBar(tab_bar_message) => self.tab_bar.update(tab_bar_message), + Message::BrowserView(browser_view) => self.browser_view.update(browser_view), } Command::none() } diff --git a/src/engines/mod.rs b/src/engines/mod.rs index 7a29db8..80a0d00 100644 --- a/src/engines/mod.rs +++ b/src/engines/mod.rs @@ -2,13 +2,14 @@ use iced::keyboard; use iced::mouse::{self, Interaction}; // use iced::widget::image::{Handle, Image}; use iced::{event::Status, Point}; +use url::Url; #[cfg(feature = "webkit")] pub mod ultralight; #[derive(Debug, Clone)] pub struct Tab { - pub url: String, + pub url: Url, pub title: String, // icon: Image, } @@ -27,10 +28,10 @@ pub trait BrowserEngine { fn get_cursor(&self) -> Interaction; // fn get_icon(&self) -> Image; fn get_title(&self) -> Option; - fn get_url(&self) -> Option; - fn goto_url(&self, url: &str); + fn get_url(&self) -> Option; + fn goto_url(&self, url: &Url); fn has_loaded(&self) -> bool; - fn new_tab(&mut self, url: &str); + fn new_tab(&mut self, url: &Url); fn goto_tab(&mut self, idx: u32) -> Option<()>; fn get_tabs(&self) -> Vec; fn current_tab(&self) -> (usize, Tab); diff --git a/src/engines/ultralight.rs b/src/engines/ultralight.rs index f156fe2..6f18785 100644 --- a/src/engines/ultralight.rs +++ b/src/engines/ultralight.rs @@ -4,10 +4,9 @@ use iced::mouse::{self, ScrollDelta}; use iced::Point; use smol_str::SmolStr; use std::sync::{Arc, RwLock}; -use ul_next::event::{self, KeyEventCreationInfo}; use ul_next::{ config::Config, - event::{MouseEvent, ScrollEvent}, + event::{self, KeyEventCreationInfo, MouseEvent, ScrollEvent}, key_code::VirtualKeyCode, platform::{self, LogLevel, Logger}, renderer::Renderer, @@ -15,6 +14,7 @@ use ul_next::{ window::Cursor, Surface, }; +use url::Url; struct UlLogger; impl Logger for UlLogger { @@ -34,7 +34,7 @@ pub struct Tab { impl From<&Tab> for super::Tab { fn from(value: &Tab) -> Self { Self { - url: value.url.read().unwrap().to_string(), + url: Url::parse(value.url.read().unwrap().as_str()).unwrap(), title: value.title.read().unwrap().to_string(), } } @@ -152,12 +152,12 @@ impl super::BrowserEngine for Ultralight { self.get_tab()?.view.title().ok() } - fn get_url(&self) -> Option { - Some(self.get_tab()?.url.read().ok()?.clone()) + fn get_url(&self) -> Option { + Some(Url::parse(self.get_tab()?.url.read().ok()?.clone().as_str()).ok()?) } - fn goto_url(&self, url: &str) { - self.get_tab().unwrap().view.load_url(url).unwrap(); + fn goto_url(&self, url: &Url) { + self.get_tab().unwrap().view.load_url(url.as_ref()).unwrap(); } fn has_loaded(&self) -> bool { @@ -174,11 +174,11 @@ impl super::BrowserEngine for Ultralight { (idx, tab.into()) } - fn new_tab(&mut self, url: &str) { + fn new_tab(&mut self, url: &Url) { if self .tabs .iter() - .filter(|tab| *tab.url.read().unwrap() == *url) + .filter(|tab| *tab.url.read().unwrap() == *url.as_ref()) .count() == 0 { @@ -188,7 +188,7 @@ impl super::BrowserEngine for Ultralight { .unwrap(); let surface = view.surface().unwrap(); - view.load_url(url).unwrap(); + view.load_url(url.as_ref()).unwrap(); // RGBA debug_assert!(surface.row_bytes() / self.width == 4); diff --git a/src/lib.rs b/src/lib.rs index 6cf3892..03a21f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ use iced::widget::image::{Handle, Image}; +use url::{ParseError, Url}; mod engines; pub use engines::BrowserEngine; @@ -9,6 +10,21 @@ pub use engines::ultralight::Ultralight; mod widgets; pub use widgets::{browser_view, nav_bar, tab_bar, BrowserView, NavBar, State, TabBar}; +fn to_url(url: &str) -> Option { + match Url::parse(url) { + Ok(url) => Some(url), + Err(error) => { + if let ParseError::RelativeUrlWithoutBase = error { + let mut base = String::from("https://"); + base.push_str(url); + Url::parse(&base).ok() + } else { + None + } + } + } +} + fn bgr_to_rgb(image: Vec) -> Vec { assert_eq!(image.len() % 4, 0); image diff --git a/src/widgets/browser_view.rs b/src/widgets/browser_view.rs index 5a73e44..8445892 100644 --- a/src/widgets/browser_view.rs +++ b/src/widgets/browser_view.rs @@ -23,9 +23,8 @@ impl BrowserView { Self(state) } - pub fn update(&mut self, message: Message) -> Action { + pub fn update(&mut self, message: Message) { match message {} - Action::None } pub fn view(&self) -> Element { diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index aefddee..85aa756 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -1,6 +1,7 @@ -use crate::engines::{self, BrowserEngine}; - use std::{cell::RefCell, rc::Rc}; +use url::Url; + +use crate::engines::{self, BrowserEngine}; pub mod browser_view; #[allow(unused)] @@ -54,7 +55,7 @@ impl State { pub fn new() -> Self { let config = Config::default(); let mut webengine = Engine::new(); - webengine.new_tab(&config.start_page); + webengine.new_tab(&Url::parse(&config.start_page).unwrap()); State { config, diff --git a/src/widgets/nav_bar.rs b/src/widgets/nav_bar.rs index f718421..ce225c2 100644 --- a/src/widgets/nav_bar.rs +++ b/src/widgets/nav_bar.rs @@ -2,13 +2,10 @@ use iced::widget::{row, text::LineHeight, text_input, tooltip, tooltip::Position use iced::{Element, Length}; use iced_aw::core::icons::bootstrap::{icon_to_text, Bootstrap}; use iced_on_focus_widget::hoverable; +use url::Url; use super::{BrowserEngine, State}; - -#[derive(Debug, Clone)] -pub enum Action { - None, -} +use crate::to_url; #[derive(Debug, Clone)] pub enum Message { @@ -42,33 +39,33 @@ impl NavBar { Self { search_focused: false, state, - url: tab.url, + url: tab.url.to_string(), } } - pub fn update(&mut self, message: Message) -> Action { + pub fn update(&mut self, message: Message) { let webengine = self.state.webengine.borrow(); match message { Message::Backward => webengine.go_back(), Message::Forward => webengine.go_forward(), Message::Refresh => webengine.refresh(), - Message::Home => webengine.goto_url(&self.state.config.start_page), + Message::Home => { + webengine.goto_url(&Url::parse(&self.state.config.start_page).unwrap()) + } Message::UrlChanged(url) => self.url = url, Message::UrlPasted(url) => { - webengine.goto_url(&url); - self.url = url; + self.url = url.clone(); + webengine.goto_url(&to_url(&url).unwrap()); } - Message::UrlSubmitted => webengine.goto_url(&self.url), + Message::UrlSubmitted => webengine.goto_url(&to_url(&self.url).unwrap()), Message::OnFocus => self.search_focused = true, Message::OnUnfocus => self.search_focused = false, } if !self.search_focused { - self.url = webengine.get_url().unwrap() + self.url = webengine.get_url().unwrap().as_ref().to_string() } - - Action::None } pub fn view(&self) -> Element { @@ -99,7 +96,7 @@ impl NavBar { let space = Space::new(Length::Fill, Length::Shrink); let space2 = Space::new(Length::Fill, Length::Shrink); let search = hoverable( - text_input("https://site.com", &self.url) + text_input("https://site.com", self.url.as_ref()) .on_input(Message::UrlChanged) .on_paste(Message::UrlPasted) .on_submit(Message::UrlSubmitted) diff --git a/src/widgets/tab_bar.rs b/src/widgets/tab_bar.rs index f0f3519..edc8072 100644 --- a/src/widgets/tab_bar.rs +++ b/src/widgets/tab_bar.rs @@ -2,14 +2,10 @@ use iced::widget::{row, tooltip, Button}; use iced::{self, Element, Length}; use iced_aw::core::icons::bootstrap::{icon_to_text, Bootstrap}; use iced_aw::{TabBar as TB, TabLabel}; +use url::Url; use super::{BrowserEngine, State}; -#[derive(Debug, Clone)] -pub enum Action { - None, -} - #[derive(Debug, Clone)] pub enum Message { TabSelected(usize), @@ -32,16 +28,16 @@ impl TabBar { Self { state } } - pub fn update(&mut self, event: Message) -> Action { + pub fn update(&mut self, event: Message) { let mut webengine = self.state.webengine.borrow_mut(); match event { Message::TabSelected(index) => webengine.goto_tab(index as u32).unwrap(), Message::TabClosed(index) => webengine.close_tab(index as u32), - Message::NewTab => webengine.new_tab(&self.state.config.start_page), + Message::NewTab => { + webengine.new_tab(&Url::parse(&self.state.config.start_page).unwrap()) + } } - - Action::None } pub fn view(&self) -> Element {