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

feat: html string attributes #368

Merged
merged 9 commits into from
Aug 12, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions .changes/load-html.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"wry": patch
---

Add html attributes as another method to load the page. This can provide some other origin header and make CORS request
possible.
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Run the `cargo run --example <file_name>` to see how each example works.
- `dragndrop`: example for file drop handler.
- `custom_titlebar`: A frameless window with custom title-bar to show `drag-region` class in action.
- `custom_protocol`: uses a custom protocol to load files from bytes.
- `html`: load the html string and load other assets with custom protocol.
- `detect_js_ecma`: detects which versions of ECMAScript is supported by the webview.
- `menu_bar`: uses a custom menu for the application in macOS and the Window and Linux/Windows.
- `system_tray`: sample tray application with different behaviours.
Expand Down
77 changes: 77 additions & 0 deletions examples/html.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

fn main() -> wry::Result<()> {
use std::fs::{canonicalize, read};

use wry::{
application::{
event::{Event, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
},
webview::WebViewBuilder,
};

let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("Hello World")
.build(&event_loop)
.unwrap();

let _webview = WebViewBuilder::new(window)
.unwrap()
// We still register custom protocol here to show that how the page with http:// origin can
// load them.
.with_custom_protocol("wry".into(), move |requested_asset_path| {
// Remove url scheme
let path = requested_asset_path.replace("wry://", "");
// Read the file content from file path
let content = read(canonicalize(&path)?)?;

// Return asset contents and mime types based on file extentions
// If you don't want to do this manually, there are some crates for you.
// Such as `infer` and `mime_guess`.
if path.ends_with(".html") {
Ok((content, String::from("text/html")))
} else if path.ends_with(".js") {
Ok((content, String::from("text/javascript")))
} else if path.ends_with(".png") {
Ok((content, String::from("image/png")))
} else {
unimplemented!();
}
})
// tell the webview to load the html string
.with_html(
r#"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<h1>Welcome to WRY!</h1>
<a href="wry://examples/hello.html">Link</a>
<script type="text/javascript" src="wry://examples/hello.js"></script>
<img src="wry://examples/icon.png" />
</body>
</html>"#,
)?
.build()?;

event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;

match event {
Event::NewEvents(StartCause::Init) => println!("Wry application started!"),
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => (),
}
});
}
57 changes: 57 additions & 0 deletions src/webview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,19 @@ pub struct WebViewAttributes {
pub transparent: bool,
/// Whether load the provided URL to [`WebView`].
pub url: Option<Url>,
/// Whether load the provided html string to [`WebView`].
/// This will be ignored if the `url` is provided.
///
/// # Warning
/// The loaded from html string will have different Origin on different platforms. And
/// servers which enforce CORS will need to add exact same Origin header in `Access-Control-Allow-Origin`
/// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the
/// different Origin headers across platforms:
///
/// - macOS: `http://localhost`
/// - Linux: `http://localhost`
/// - Windows: `null`
pub html: Option<String>,
/// Initialize javascript code when loading new pages. When webview load a new page, this
/// initialization code will be executed. It is guaranteed that code is executed before
/// `window.onload`.
Expand All @@ -60,6 +73,19 @@ pub struct WebViewAttributes {
///
/// The closure takes a url string slice, and returns a two item tuple of a vector of
/// bytes which is the content and a mimetype string of the content.
///
/// # Warning
/// Pages loaded from custom protocol will have different Origin on different platforms. And
/// servers which enforce CORS will need to add exact same Origin header in `Access-Control-Allow-Origin`
/// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the
/// different Origin headers across platforms:
///
/// - macOS: `<scheme_name>://<path>` (so it will be `wry://examples` in `custom_protocol` example)
/// - Linux: Though it's same as macOS, there's a [bug] that Origin header in the request will be
/// empty. So the only way to pass the server is setting `Access-Control-Allow-Origin: *`.
/// - Windows: `https://<scheme_name>.<path>` (so it will be `https://wry.examples` in `custom_protocol` example)
///
/// [bug]: https://bugs.webkit.org/show_bug.cgi?id=229034
pub custom_protocols: Vec<(String, Box<dyn Fn(&str) -> Result<(Vec<u8>, String)>>)>,
/// Set the RPC handler to Communicate between the host Rust code and Javascript on webview.
///
Expand Down Expand Up @@ -91,6 +117,7 @@ impl Default for WebViewAttributes {
visible: true,
transparent: false,
url: None,
html: None,
initialization_scripts: vec![],
custom_protocols: vec![],
rpc_handler: None,
Expand Down Expand Up @@ -148,6 +175,19 @@ impl<'a> WebViewBuilder<'a> {
///
/// The closure takes a url string slice, and returns a two item tuple of a
/// vector of bytes which is the content and a mimetype string of the content.
///
/// # Warning
/// Pages loaded from custom protocol will have different Origin on different platforms. And
/// servers which enforce CORS will need to add exact same Origin header in `Access-Control-Allow-Origin`
/// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the
/// different Origin headers across platforms:
///
/// - macOS: `<scheme_name>://<path>` (so it will be `wry://examples` in `custom_protocol` example)
/// - Linux: Though it's same as macOS, there's a [bug] that Origin header in the request will be
/// empty. So the only way to pass the server is setting `Access-Control-Allow-Origin: *`.
/// - Windows: `https://<scheme_name>.<path>` (so it will be `https://wry.examples` in `custom_protocol` example)
///
/// [bug]: https://bugs.webkit.org/show_bug.cgi?id=229034
#[cfg(feature = "protocol")]
pub fn with_custom_protocol<F>(mut self, name: String, handler: F) -> Self
where
Expand Down Expand Up @@ -201,6 +241,23 @@ impl<'a> WebViewBuilder<'a> {
Ok(self)
}

/// Load the provided HTML string when the builder calling [`WebViewBuilder::build`] to create the
/// [`WebView`]. This will be ignored if `url` is already provided.
///
/// # Warning
/// The Page loaded from html string will have different Origin on different platforms. And
/// servers which enforce CORS will need to add exact same Origin header in `Access-Control-Allow-Origin`
/// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the
/// different Origin headers across platforms:
///
/// - macOS: `http://localhost`
/// - Linux: `http://localhost`
/// - Windows: `null`
pub fn with_html(mut self, html: impl Into<String>) -> Result<Self> {
self.webview.html = Some(html.into());
Ok(self)
}

/// Set the web context that can share with multiple [`WebView`]s.
pub fn with_web_context(mut self, web_context: &'a mut WebContext) -> Self {
self.web_context = Some(web_context);
Expand Down
2 changes: 2 additions & 0 deletions src/webview/webkitgtk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ impl InnerWebView {
if let Some(url) = attributes.url {
web_context.queue_load_uri(Rc::clone(&w.webview), url);
web_context.flush_queue_loader();
} else if let Some(html) = attributes.html {
w.webview.load_html(&html, Some("http://localhost"));
}

Ok(w)
Expand Down
2 changes: 2 additions & 0 deletions src/webview/webview2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ impl InnerWebView {
}
w.navigate(&url_string)?;
}
} else if let Some(html) = attributes.html {
w.navigate_to_string(&html)?;
}

controller.put_is_visible(true)?;
Expand Down
8 changes: 5 additions & 3 deletions src/webview/wkwebview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ impl InnerWebView {
} else {
w.navigate(url.as_str());
}
} else if let Some(html) = attributes.html {
w.navigate_to_string(&html);
}

// Inject the web view into the window as main content
Expand Down Expand Up @@ -338,11 +340,11 @@ impl InnerWebView {
}
}

fn navigate_to_string(&self, url: &str) {
fn navigate_to_string(&self, html: &str) {
// Safety: objc runtime calls are unsafe
unsafe {
let empty: id = msg_send![class!(NSURL), URLWithString: NSString::new("")];
let () = msg_send![self.webview, loadHTMLString:NSString::new(url) baseURL:empty];
let url: id = msg_send![class!(NSURL), URLWithString: NSString::new("http://localhost")];
let () = msg_send![self.webview, loadHTMLString:NSString::new(html) baseURL:url];
}
}

Expand Down