Skip to content

Commit

Permalink
Add custom protocol (#65)
Browse files Browse the repository at this point in the history
* feat: add register_buffer_protocol on linux

* errmmm..windows kinda

* progress???

* IT WORKS

* Update signature of uri_to_stream_async

* fix(Cargo.toml): remove duplicate key

* feat: custom protocol on windowsssssss!!!!

* feat: update custom protocol on linux

* Add protocol param to mac

* Remove html file

Co-authored-by: Noah Klayman <noahklayman@gmail.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
  • Loading branch information
3 people authored Feb 23, 2021
1 parent b963030 commit 7a49280
Show file tree
Hide file tree
Showing 9 changed files with 236 additions and 30 deletions.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,22 @@ serde_json = "1.0"
thiserror = "1.0"
url = "2.2"
image = "0.23"
mime_guess = "2.0.3"

[target.'cfg(target_os = "linux")'.dependencies]
cairo-rs = "0.9"
webkit2gtk = { version = "0.11", features = ["v2_8"] }
gio = "0.9"
glib = "0.10.3"
gtk = "0.9"
gdk = "0.13"
gdk-pixbuf = "0.9"
glib = "0.10"

[target.'cfg(target_os = "windows")'.dependencies]
webview2 = "0.1.0-beta.1"
winapi = { version = "0.3", features = ["libloaderapi"] }
winit = "0.24"
winrt = "0.7.2"

[target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.24"
Expand Down
35 changes: 29 additions & 6 deletions src/application/general.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
application::{App, AppProxy, InnerWebViewAttributes, InnerWindowAttributes},
ApplicationProxy, Attributes, Callback, Error, Icon, Message, Result, WebView, WebViewBuilder,
WindowMessage, WindowProxy,
WindowMessage, WindowProxy, CustomProtocol
};
#[cfg(target_os = "macos")]
use winit::platform::macos::{ActivationPolicy, WindowBuilderExtMacOS};
Expand Down Expand Up @@ -48,9 +48,15 @@ impl AppProxy for InnerApplicationProxy {
&self,
attributes: Attributes,
callbacks: Option<Vec<Callback>>,
custom_protocol: Option<CustomProtocol>,
) -> Result<WindowId> {
let (sender, receiver) = channel();
self.send_message(Message::NewWindow(attributes, callbacks, sender))?;
self.send_message(Message::NewWindow(
attributes,
callbacks,
sender,
custom_protocol,
))?;
Ok(receiver.recv()?)
}
}
Expand Down Expand Up @@ -118,10 +124,17 @@ impl App for InnerApplication {
&mut self,
attributes: Attributes,
callbacks: Option<Vec<Callback>>,
custom_protocol: Option<CustomProtocol>,
) -> Result<Self::Id> {
let (window_attrs, webview_attrs) = attributes.split();
let window = _create_window(&self.event_loop, window_attrs)?;
let webview = _create_webview(&self.application_proxy(), window, webview_attrs, callbacks)?;
let webview = _create_webview(
&self.application_proxy(),
window,
webview_attrs,
callbacks,
custom_protocol,
)?;
let id = webview.window().id();
self.webviews.insert(id, webview);
Ok(id)
Expand Down Expand Up @@ -157,12 +170,18 @@ impl App for InnerApplication {
_ => {}
},
Event::UserEvent(message) => match message {
Message::NewWindow(attributes, callbacks, sender) => {
Message::NewWindow(attributes, callbacks, sender, custom_protocol) => {
let (window_attrs, webview_attrs) = attributes.split();
let window = _create_window(&event_loop, window_attrs).unwrap();
sender.send(window.id()).unwrap();
let webview =
_create_webview(&dispatcher, window, webview_attrs, callbacks).unwrap();
let webview = _create_webview(
&dispatcher,
window,
webview_attrs,
callbacks,
custom_protocol,
)
.unwrap();
let id = webview.window().id();
windows.insert(id, webview);
}
Expand Down Expand Up @@ -320,6 +339,7 @@ fn _create_webview(
window: Window,
attributes: InnerWebViewAttributes,
callbacks: Option<Vec<Callback>>,
custom_protocol: Option<CustomProtocol>,
) -> Result<WebView> {
let window_id = window.id();
let mut webview = WebViewBuilder::new(window)?.transparent(attributes.transparent);
Expand All @@ -343,6 +363,9 @@ fn _create_webview(
});
}
}
if let Some(protocol) = custom_protocol {
webview = webview.register_protocol(protocol.name, protocol.handler)
}
webview = match attributes.url {
Some(url) => webview.load_url(&url)?,
None => webview,
Expand Down
35 changes: 29 additions & 6 deletions src/application/gtkrs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
application::{App, AppProxy, InnerWebViewAttributes, InnerWindowAttributes, WindowProxy},
ApplicationProxy, Attributes, Callback, Error, Icon, Message, Result, WebView, WebViewBuilder,
WindowMessage,
WindowMessage, CustomProtocol
};

use std::{
Expand Down Expand Up @@ -46,9 +46,15 @@ impl AppProxy for InnerApplicationProxy {
&self,
attributes: Attributes,
callbacks: Option<Vec<Callback>>,
custom_protocol: Option<CustomProtocol>,
) -> Result<WindowId> {
let (sender, receiver): (Sender<WindowId>, Receiver<WindowId>) = channel();
self.send_message(Message::NewWindow(attributes, callbacks, sender))?;
self.send_message(Message::NewWindow(
attributes,
callbacks,
sender,
custom_protocol,
))?;
Ok(receiver.recv()?)
}
}
Expand Down Expand Up @@ -83,10 +89,17 @@ impl App for InnerApplication {
&mut self,
attributes: Attributes,
callbacks: Option<Vec<Callback>>,
custom_protocol: Option<CustomProtocol>,
) -> Result<Self::Id> {
let (window_attrs, webview_attrs) = attributes.split();
let window = _create_window(&self.app, window_attrs)?;
let webview = _create_webview(&self.application_proxy(), window, webview_attrs, callbacks)?;
let webview = _create_webview(
&self.application_proxy(),
window,
webview_attrs,
callbacks,
custom_protocol,
)?;
let id = webview.window().get_id();
self.webviews.insert(id, webview);

Expand Down Expand Up @@ -131,12 +144,18 @@ impl App for InnerApplication {

while let Ok(message) = self.event_loop_proxy_rx.try_recv() {
match message {
Message::NewWindow(attributes, callbacks, sender) => {
Message::NewWindow(attributes, callbacks, sender, custom_protocol) => {
let (window_attrs, webview_attrs) = attributes.split();
let window = _create_window(&self.app, window_attrs).unwrap();
sender.send(window.get_id()).unwrap();
let webview =
_create_webview(&proxy, window, webview_attrs, callbacks).unwrap();
let webview = _create_webview(
&proxy,
window,
webview_attrs,
callbacks,
custom_protocol,
)
.unwrap();
let id = webview.window().get_id();
let shared_webviews_ = shared_webviews_.clone();
webview
Expand Down Expand Up @@ -366,6 +385,7 @@ fn _create_webview(
window: ApplicationWindow,
attributes: InnerWebViewAttributes,
callbacks: Option<Vec<Callback>>,
custom_protocol: Option<CustomProtocol>,
) -> Result<WebView> {
let window_id = window.get_id();
let mut webview = WebViewBuilder::new(window)?.transparent(attributes.transparent);
Expand Down Expand Up @@ -393,6 +413,9 @@ fn _create_webview(
Some(url) => webview.load_url(&url)?,
None => webview,
};
if let Some(protocol) = custom_protocol {
webview = webview.register_protocol(protocol.name, protocol.handler);
}

let webview = webview.build()?;
Ok(webview)
Expand Down
32 changes: 29 additions & 3 deletions src/application/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ impl std::fmt::Debug for Callback {
}
}

pub struct CustomProtocol {
pub name: String,
pub handler: Box<dyn Fn(&str) -> Result<Vec<u8>> + Send>,
}

impl std::fmt::Debug for CustomProtocol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CustomProtocol")
.field("name", &self.name)
.finish()
}
}

/// An icon used for the window title bar, taskbar, etc.
#[derive(Debug, Clone)]
pub struct Icon(pub(crate) Vec<u8>);
Expand Down Expand Up @@ -280,7 +293,12 @@ pub enum WindowMessage {
#[derive(Debug)]
pub enum Message {
Window(WindowId, WindowMessage),
NewWindow(Attributes, Option<Vec<Callback>>, Sender<WindowId>),
NewWindow(
Attributes,
Option<Vec<Callback>>,
Sender<WindowId>,
Option<CustomProtocol>,
),
}

/// A proxy to sent custom messages to [`Application`].
Expand All @@ -304,8 +322,11 @@ impl ApplicationProxy {
&self,
attributes: Attributes,
callbacks: Option<Vec<Callback>>,
custom_protocol: Option<CustomProtocol>,
) -> Result<WindowProxy> {
let id = self.inner.add_window(attributes, callbacks)?;
let id = self
.inner
.add_window(attributes, callbacks, custom_protocol)?;
Ok(WindowProxy::new(self.clone(), id))
}
}
Expand All @@ -316,6 +337,7 @@ trait AppProxy {
&self,
attributes: Attributes,
callbacks: Option<Vec<Callback>>,
custom_protocol: Option<CustomProtocol>,
) -> Result<WindowId>;
}

Expand Down Expand Up @@ -515,8 +537,11 @@ impl Application {
&mut self,
attributes: Attributes,
callbacks: Option<Vec<Callback>>,
custom_protocol: Option<CustomProtocol>,
) -> Result<WindowProxy> {
let id = self.inner.create_webview(attributes, callbacks)?;
let id = self
.inner
.create_webview(attributes, callbacks, custom_protocol)?;
Ok(self.window_proxy(id))
}

Expand Down Expand Up @@ -550,6 +575,7 @@ trait App: Sized {
&mut self,
attributes: Attributes,
callbacks: Option<Vec<Callback>>,
custom_protocol: Option<CustomProtocol>,
) -> Result<Self::Id>;

fn application_proxy(&self) -> Self::Proxy;
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ pub mod platform;
pub mod webview;

pub use application::{
Application, ApplicationProxy, Attributes, Callback, Icon, Message, WindowId, WindowMessage,
WindowProxy,
Application, ApplicationProxy, Attributes, Callback, CustomProtocol, Icon, Message, WindowId,
WindowMessage, WindowProxy,
};
pub use serde_json::Value;
pub(crate) use webview::{Dispatcher, WebView, WebViewBuilder};
Expand Down
45 changes: 41 additions & 4 deletions src/platform/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ use std::rc::Rc;

use gdk::RGBA;
use gio::Cancellable;
use glib::{Bytes, FileError};
use gtk::{ApplicationWindow as Window, ApplicationWindowExt, ContainerExt, WidgetExt};
use url::Url;
use webkit2gtk::{
SettingsExt, UserContentInjectedFrames, UserContentManager, UserContentManagerExt, UserScript,
UserScriptInjectionTime, WebView, WebViewExt,
SecurityManagerExt, SettingsExt, URISchemeRequestExt, UserContentInjectedFrames,
UserContentManager, UserContentManagerExt, UserScript, UserScriptInjectionTime, WebContext,
WebContextExt, WebView, WebViewExt, WebViewExtManual,
};

pub struct InnerWebView {
Expand All @@ -20,15 +22,19 @@ pub struct InnerWebView {
impl WV for InnerWebView {
type Window = Window;

fn new(
fn new<F: 'static + Fn(&str) -> Result<Vec<u8>>>(
window: &Window,
scripts: Vec<String>,
url: Option<Url>,
transparent: bool,
custom_protocol: Option<(String, F)>,
) -> Result<Self> {
// Webview widget
let manager = UserContentManager::new();
let webview = Rc::new(WebView::with_user_content_manager(&manager));
let context = WebContext::new();
let webview = Rc::new(WebView::new_with_context_and_user_content_manager(
&context, &manager,
));

// Message handler
let wv = Rc::clone(&webview);
Expand Down Expand Up @@ -113,6 +119,37 @@ impl WV for InnerWebView {
w.init(&js)?;
}

// Custom protocol
if let Some((name, handler)) = custom_protocol {
context
.get_security_manager()
.unwrap()
.register_uri_scheme_as_secure(&name);
context.register_uri_scheme(&name.clone(), move |request| {
let file_path = request
.get_uri()
.unwrap()
.as_str()
.replace(format!("{}://", name).as_str(), "")
// Somehow other assets get index.html in their path
.replace("index.html/", "");
let mime = mime_guess::from_path(&file_path)
.first()
.unwrap()
.to_string();
match handler(&file_path) {
Ok(buffer) => {
let input = gio::MemoryInputStream::from_bytes(&Bytes::from(&buffer));
request.finish(&input, buffer.len() as i64, Some(&mime))
}
Err(_) => request.finish_error(&mut glib::Error::new(
FileError::Exist,
"Could not get requested file.",
)),
}
});
}

// Navigation
if let Some(url) = url {
w.webview.load_uri(url.as_str());
Expand Down
Loading

0 comments on commit 7a49280

Please sign in to comment.