Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to follow links #173

Merged
merged 5 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions apps/readme/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod readme_application;

use blitz_html::HtmlDocument;
use blitz_net::Provider;
use blitz_traits::navigation::NavigationProvider;
use markdown::{markdown_to_html, BLITZ_MD_STYLES, GITHUB_MD_STYLES};
use notify::{Error as NotifyError, Event as NotifyEvent, RecursiveMode, Watcher as _};
use readme_application::{ReadmeApplication, ReadmeEvent};
Expand All @@ -14,9 +15,21 @@ use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use url::Url;
use winit::event_loop::EventLoopProxy;
use winit::window::WindowAttributes;

const USER_AGENT: &str = "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/81.0";

struct ReadmeNavigationProvider {
proxy: EventLoopProxy<BlitzEvent>,
}

impl NavigationProvider for ReadmeNavigationProvider {
fn navigate_new_page(&self, url: String) {
let _ = self.proxy.send_event(BlitzEvent::Navigate(url));
}
}

fn main() {
let raw_url = std::env::args().nth(1).unwrap_or_else(|| {
let cwd = current_dir().unwrap();
Expand Down Expand Up @@ -55,19 +68,30 @@ fn main() {
let net_callback = BlitzShellNetCallback::shared(proxy.clone());
let net_provider = Provider::shared(net_callback);

let proxy = event_loop.create_proxy();
let navigation_provider = ReadmeNavigationProvider {
proxy: proxy.clone(),
};
let navigation_provider = Arc::new(navigation_provider);

let doc = HtmlDocument::from_html(
&html,
Some(base_url),
stylesheets,
net_provider.clone(),
None,
navigation_provider.clone(),
);
let attrs = WindowAttributes::default().with_title(title);
let window = WindowConfig::with_attributes(doc, attrs);

// Create application
let mut application =
ReadmeApplication::new(event_loop.create_proxy(), raw_url.clone(), net_provider);
let mut application = ReadmeApplication::new(
proxy.clone(),
raw_url.clone(),
net_provider,
navigation_provider,
);
application.add_window(window);

if let Some(path) = file_path {
Expand Down
9 changes: 9 additions & 0 deletions apps/readme/src/readme_application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use blitz_dom::net::Resource;
use blitz_html::HtmlDocument;
use blitz_renderer_vello::BlitzVelloRenderer;
use blitz_shell::{BlitzApplication, BlitzEvent, View, WindowConfig};
use blitz_traits::navigation::NavigationProvider;
use blitz_traits::net::NetProvider;
use tokio::runtime::Handle;
use winit::application::ApplicationHandler;
Expand All @@ -23,13 +24,15 @@ pub struct ReadmeApplication {
net_provider: Arc<dyn NetProvider<Data = Resource>>,
raw_url: String,
keyboard_modifiers: Modifiers,
navigation_provider: Arc<dyn NavigationProvider>,
}

impl ReadmeApplication {
pub fn new(
proxy: EventLoopProxy<BlitzEvent>,
raw_url: String,
net_provider: Arc<dyn NetProvider<Data = Resource>>,
navigation_provider: Arc<dyn NavigationProvider>,
) -> Self {
let handle = Handle::current();
Self {
Expand All @@ -38,6 +41,7 @@ impl ReadmeApplication {
raw_url,
net_provider,
keyboard_modifiers: Default::default(),
navigation_provider,
}
}

Expand Down Expand Up @@ -66,6 +70,7 @@ impl ReadmeApplication {
stylesheets,
self.net_provider.clone(),
None,
self.navigation_provider.clone(),
);
self.window_mut().replace_document(doc);
}
Expand Down Expand Up @@ -124,6 +129,10 @@ impl ApplicationHandler<BlitzEvent> for ReadmeApplication {
self.reload_document();
}
}
BlitzEvent::Navigate(url) => {
self.raw_url = url;
self.reload_document();
}
event => self.inner.user_event(event_loop, event),
}
}
Expand Down
1 change: 1 addition & 0 deletions apps/wpt/src/attr_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub async fn parse_and_resolve_document(
Vec::new(),
Arc::clone(&ctx.net_provider) as SharedProvider<Resource>,
Some(clone_font_ctx(&ctx.font_ctx)),
ctx.navigation_provider.clone(),
);

document.as_mut().set_viewport(ctx.viewport.clone());
Expand Down
4 changes: 4 additions & 0 deletions apps/wpt/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use blitz_dom::net::Resource;
use blitz_renderer_vello::VelloImageRenderer;
use blitz_traits::navigation::{DummyNavigationProvider, NavigationProvider};
use blitz_traits::{ColorScheme, Viewport};
use parley::FontContext;
use pollster::FutureExt as _;
Expand Down Expand Up @@ -170,6 +171,7 @@ impl Buffers {
struct ThreadCtx {
viewport: Viewport,
net_provider: Arc<WptNetProvider<Resource>>,
navigation_provider: Arc<dyn NavigationProvider>,
renderer: VelloImageRenderer,
font_ctx: FontContext,
buffers: Buffers,
Expand Down Expand Up @@ -327,6 +329,7 @@ fn main() {
.unwrap();

let dummy_base_url = Url::parse("http://dummy.local").unwrap();
let navigation_provider = Arc::new(DummyNavigationProvider);

RefCell::new(ThreadCtx {
viewport,
Expand All @@ -349,6 +352,7 @@ fn main() {
out_dir: out_dir.clone(),
wpt_dir: wpt_dir.clone(),
dummy_base_url,
navigation_provider,
})
})
.borrow_mut();
Expand Down
1 change: 1 addition & 0 deletions apps/wpt/src/ref_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ async fn render_html_to_buffer(
Vec::new(),
Arc::clone(&ctx.net_provider) as SharedProvider<Resource>,
Some(clone_font_ctx(&ctx.font_ctx)),
ctx.navigation_provider.clone(),
);

document.as_mut().set_viewport(ctx.viewport.clone());
Expand Down
32 changes: 28 additions & 4 deletions packages/blitz-dom/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::stylo_to_cursor_icon::stylo_to_cursor_icon;
use crate::util::ImageType;
use crate::{ElementNodeData, Node, NodeData, TextNodeData};
use app_units::Au;
use blitz_traits::navigation::{DummyNavigationProvider, NavigationProvider};
use blitz_traits::net::{DummyNetProvider, SharedProvider};
use blitz_traits::{ColorScheme, Viewport};
use cursor_icon::CursorIcon;
Expand Down Expand Up @@ -145,6 +146,10 @@ pub struct Document {

/// Network provider. Can be used to fetch assets.
pub net_provider: SharedProvider<Resource>,

/// Navigation provider. Can be used to navigate to a new page (bubbles up the event
/// on e.g. clicking a Link)
pub navigation_provider: Arc<dyn NavigationProvider>,
}

fn make_device(viewport: &Viewport) -> Device {
Expand Down Expand Up @@ -222,6 +227,13 @@ impl DocumentLike for Document {
}
self.set_focus_to(node_id);
}
} else if el.name.local == local_name!("a") {
if let Some(href) = el.attr(local_name!("href")) {
nerdachse marked this conversation as resolved.
Show resolved Hide resolved
let url = resolve_url(&self.base_url, href);
self.navigation_provider.navigate_new_page(url.into());
} else {
println!("Clicked link without href: {:?}", el.attrs());
}
}
}
}
Expand Down Expand Up @@ -331,6 +343,7 @@ impl Document {
focus_node_id: None,
changed: HashSet::new(),
net_provider: Arc::new(DummyNetProvider::default()),
navigation_provider: Arc::new(DummyNavigationProvider {}),
};

// Initialise document with root Document node
Expand All @@ -357,6 +370,11 @@ impl Document {
self.net_provider = net_provider;
}

/// Set the Document's navigation provider
pub fn set_navigation_provider(&mut self, navigation_provider: Arc<dyn NavigationProvider>) {
self.navigation_provider = navigation_provider;
}

/// Set base url for resolving linked resources (stylesheets, images, fonts, etc)
pub fn set_base_url(&mut self, url: &str) {
self.base_url = Some(Url::parse(url).unwrap());
Expand Down Expand Up @@ -586,10 +604,7 @@ impl Document {
}

pub fn resolve_url(&self, raw: &str) -> url::Url {
match &self.base_url {
Some(base_url) => base_url.join(raw).unwrap(),
None => url::Url::parse(raw).unwrap(),
}
resolve_url(&self.base_url, raw)
}

pub fn print_tree(&self) {
Expand Down Expand Up @@ -1224,3 +1239,12 @@ impl AsMut<Document> for Document {
self
}
}

fn resolve_url(base_url: &Option<url::Url>, raw: &str) -> url::Url {
// TODO: this will bite use eventually. You can be sure on the www there is a myriad of links that
nerdachse marked this conversation as resolved.
Show resolved Hide resolved
// cannot be parsed.
match base_url {
Some(base_url) => base_url.join(raw).unwrap(),
None => url::Url::parse(raw).unwrap(),
}
}
10 changes: 8 additions & 2 deletions packages/blitz-html/src/html_document.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::sync::Arc;

use crate::DocumentHtmlParser;

use blitz_dom::{
events::RendererEvent, net::Resource, Document, DocumentLike, FontContext, DEFAULT_CSS,
};
use blitz_traits::{net::SharedProvider, ColorScheme, Viewport};
use blitz_traits::{navigation::NavigationProvider, net::SharedProvider, ColorScheme, Viewport};

pub struct HtmlDocument {
inner: Document,
Expand All @@ -28,7 +30,7 @@ impl From<HtmlDocument> for Document {
}
impl DocumentLike for HtmlDocument {
fn handle_event(&mut self, event: RendererEvent) {
self.inner.as_mut().handle_event(event);
self.inner.as_mut().handle_event(event)
}
}

Expand All @@ -39,6 +41,7 @@ impl HtmlDocument {
stylesheets: Vec<String>,
net_provider: SharedProvider<Resource>,
font_ctx: Option<FontContext>,
navigation_provider: Arc<dyn NavigationProvider>,
) -> Self {
// Spin up the virtualdom and include the default stylesheet
let viewport = Viewport::new(0, 0, 1.0, ColorScheme::Light);
Expand All @@ -55,6 +58,9 @@ impl HtmlDocument {
// Set the net provider
doc.set_net_provider(net_provider.clone());

// Set the navigation provider
doc.set_navigation_provider(navigation_provider.clone());

// Include default and user-specified stylesheets
doc.add_user_agent_stylesheet(DEFAULT_CSS);
for ss in &stylesheets {
Expand Down
3 changes: 3 additions & 0 deletions packages/blitz-shell/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ impl<Doc: DocumentLike, Rend: DocumentRenderer> ApplicationHandler<BlitzEvent>
BlitzEvent::Embedder(_) => {
// Do nothing. Should be handled by embedders (if required).
}
BlitzEvent::Navigate(_url) => {
// Do nothing. Should be handled by embedders (if required).
}
}
}
}
3 changes: 3 additions & 0 deletions packages/blitz-shell/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ pub enum BlitzEvent {

/// An arbitary event from the Blitz embedder
Embedder(Arc<dyn Any + Send + Sync>),

/// Navigate to another URL (triggered by e.g. clicking a link)
Navigate(String),
nerdachse marked this conversation as resolved.
Show resolved Hide resolved
}
impl BlitzEvent {
pub fn embedder_event<T: Any + Send + Sync>(value: T) -> Self {
Expand Down
2 changes: 2 additions & 0 deletions packages/blitz-traits/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub mod net;

pub mod navigation;

mod devtools;
pub use devtools::Devtools;

Expand Down
12 changes: 12 additions & 0 deletions packages/blitz-traits/src/navigation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// A provider to enable a document to bubble up navigation events (e.g. clicking a link)
pub trait NavigationProvider: Send + Sync + 'static {
nicoburns marked this conversation as resolved.
Show resolved Hide resolved
fn navigate_new_page(&self, url: String);
}

pub struct DummyNavigationProvider;

impl NavigationProvider for DummyNavigationProvider {
fn navigate_new_page(&self, _url: String) {
// Default impl: do nothing
}
}
1 change: 1 addition & 0 deletions packages/blitz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ blitz-renderer-vello = { path = "../blitz-renderer-vello" }
blitz-html = { path = "../blitz-html" }
blitz-shell = { path = "../blitz-shell" }
blitz-net = { path = "../blitz-net", optional = true }
blitz-traits = { path = "../blitz-traits" }
nerdachse marked this conversation as resolved.
Show resolved Hide resolved

# IO & Networking
url = { workspace = true, features = ["serde"], optional = true }
Expand Down
14 changes: 13 additions & 1 deletion packages/blitz/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
//! - `menu`: Enables the [`muda`] menubar.
//! - `tracing`: Enables tracing support.

use std::sync::Arc;

use blitz_html::HtmlDocument;
use blitz_renderer_vello::BlitzVelloRenderer;
use blitz_shell::{
create_default_event_loop, BlitzApplication, BlitzEvent, BlitzShellNetCallback, Config,
WindowConfig,
};
use blitz_traits::navigation::DummyNavigationProvider;

#[cfg(feature = "net")]
pub fn launch_url(url: &str) {
Expand Down Expand Up @@ -74,7 +77,16 @@ fn launch_internal(html: &str, cfg: Config) {
Arc::new(DummyNetProvider::default())
};

let doc = HtmlDocument::from_html(html, cfg.base_url, cfg.stylesheets, net_provider, None);
let navigation_provider = Arc::new(DummyNavigationProvider);

let doc = HtmlDocument::from_html(
html,
cfg.base_url,
cfg.stylesheets,
net_provider,
None,
navigation_provider,
);
let window: WindowConfig<HtmlDocument, BlitzVelloRenderer> = WindowConfig::new(doc);

// Create application
Expand Down
Loading