Giter VIP home page Giter VIP logo

wry's Introduction

WRY Webview Rendering library

License Chat Server website https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg support

Cross-platform WebView rendering library in Rust that supports all major desktop platforms like Windows, macOS, and Linux.

Overview

WRY connects the web engine on each platform and provides easy to use and unified interface to render WebView. The webview requires a running event loop and a window type that implements HasWindowHandle, or a gtk container widget if you need to support X11 and Wayland. You can use a windowing library like tao or winit.

Usage

The minimum example to create a Window and browse a website looks like following:

fn main() -> wry::Result<()> {
  use tao::{
    event::{Event, StartCause, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
    window::WindowBuilder,
  };
  use wry::WebViewBuilder;

  let event_loop = EventLoop::new();
  let window = WindowBuilder::new()
    .with_title("Hello World")
    .build(&event_loop)
    .unwrap();
  let _webview = WebViewBuilder::new(&window)
    .with_url("https://tauri.app")
    .build()?;

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

    match event {
      Event::NewEvents(StartCause::Init) => println!("Wry has started!"),
      Event::WindowEvent {
        event: WindowEvent::CloseRequested,
        ..
      } => *control_flow = ControlFlow::Exit,
      _ => (),
    }
  });
}

There are also more samples under examples, you can enter commands like the following to try them:

cargo run --example multiwindow

For more information, please read the documentation below.

Platform-specific notes

Here is the underlying web engine each platform uses, and some dependencies you might need to install.

Linux

Wry also needs WebKitGTK for WebView. So please make sure the following packages are installed:

Arch Linux / Manjaro:

sudo pacman -S webkit2gtk-4.1

The libayatana-indicator package can be installed from the Arch User Repository (AUR).

Debian / Ubuntu:

sudo apt install libwebkit2gtk-4.1-dev

Fedora

sudo dnf install gtk3-devel webkit2gtk4.1-devel

Fedora does not have the Ayatana package yet, so you need to use the GTK one, see the feature flags documentation.

macOS

WebKit is native on macOS so everything should be fine.

If you are cross-compiling for macOS using osxcross and encounter a runtime panic like Class with name WKWebViewConfiguration could not be found it's possible that WebKit.framework has not been linked correctly, to fix this set the RUSTFLAGS environment variable:

RUSTFLAGS="-l framework=WebKit" cargo build --target=x86_64-apple-darwin --release

Windows

WebView2 provided by Microsoft Edge Chromium is used. So wry supports Windows 7, 8, 10 and 11.

Android / iOS

Wry supports mobile with the help of cargo-mobile2 CLI to create template project. If you are interested in playing or hacking it, please follow MOBILE.md.

If you wish to create Android project yourself, there is a few requirements that your application needs to uphold:

  1. You need to set a few environment variables that will be used to generate the necessary kotlin files that you need to include in your Android application for wry to function properly:

    • WRY_ANDROID_PACKAGE: which is the reversed domain name of your android project and the app name in snake_case, for example, com.wry.example.wry_app
    • WRY_ANDROID_LIBRARY: for example, if your cargo project has a lib name wry_app, it will generate libwry_app.so so you se this env var to wry_app
    • WRY_ANDROID_KOTLIN_FILES_OUT_DIR: for example, path/to/app/src/main/kotlin/com/wry/example
  2. Your main Android Activity needs to inherit AppCompatActivity, preferably it should use the generated WryActivity or inherit it.

  3. Your Rust app needs to call wry::android_setup function to setup the necessary logic to be able to create webviews later on.

  4. Your Rust app needs to call wry::android_binding! macro to setup the JNI functions that will be called by WryActivity and various other places.

It is recommended to use tao crate as it provides maximum compatibility with wry

#[cfg(target_os = "android")]
{
  tao::android_binding!(
      com_example,
      wry_app,
      WryActivity,
      wry::android_setup, // pass the wry::android_setup function to tao which will invoke when the event loop is created
      _start_app
  );
  wry::android_binding!(com_example, ttt);
}
  • WRY_ANDROID_PACKAGE which is the reversed domain name of your android project and the app name in snake_case for example: com.wry.example.wry_app
  • WRY_ANDROID_LIBRARY for example: if your cargo project has a lib name wry_app, it will generate libwry_app.so so you set this env var to wry_app
  • WRY_ANDROID_KOTLIN_FILES_OUT_DIR for example: path/to/app/src/main/kotlin/com/wry/example

Partners

CrabNebula

For the complete list of sponsors please visit our website and Open Collective.

License

Apache-2.0/MIT

wry's People

Contributors

ahodesuka avatar amrbashir avatar atlanticaccent avatar bg5hfc avatar chippers avatar dannyhuangxd avatar dklassic avatar dvc94ch avatar fabianlars avatar github-actions[bot] avatar jbolda avatar jensmertelmeyer avatar jhutchins avatar jonaskruckenberg avatar keiya01 avatar lemarier avatar lorenzolewis avatar lucasfernog avatar nklayman avatar nothingismagick avatar perbothner avatar pewsheen avatar renovate[bot] avatar rhysd avatar simonhyll avatar spwilson2 avatar thewh1teagle avatar tmpfs avatar wravery avatar wusyong avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wry's Issues

OAuth2 authorization code flow example with CustomProtocol

I've tried to complete an OAuth2 authorization code flow with wry and it worked! It demonstrates the CustomProtocol for the redirect URI with a custom scheme as requested in #39 . This is great because the alternative is either a device code flow, which is clumsy, or starting your own web server just to receive the redirect URL information...

Describe the solution you'd like
I'd like the example to be added, perhaps a little more polished.

Describe alternatives you've considered

  • Publishing a crate with this functionality. It is not something I'd like to commit to right now.
  • Another great feature would be to intercept web requests and perhaps rewrite them or serve them from the back-end. Any redirect-uri could be used then.

Would you assign yourself to implement this feature?

  • Yes, if welcome
  • No

Additional context
The tool opens a full screen authentication dialogue. One has to provide valid your-tenant-id, your-client-id, redirect-uri, ... After successful authentication or on failure, the tool receives the redirect URL and prints it to the console.

/*!

Example:

  oauthorize -a https://login.microsoftonline.com/your-tenant-id/oauth2/v2.0/authorize -c your-client-id -r urn:authenticate -s email -s profile -s openid

*/

use async_std::task::block_on;
use http_types::{Body, Url};
use log::*;
use serde::{Deserialize, Serialize};
use std::{
    sync::mpsc::{channel, Sender},
    time::Duration,
};
use structopt::StructOpt;
use wry::{Application, Attributes, CustomProtocol, RpcRequest, RpcResponse, WindowProxy};

fn main() -> anyhow::Result<()> {
    env_logger::init();
    let opts = Opts::from_args();

    let (redirect_sender, redirect_receiver) = channel();
    
    authenticate(
        opts.authorize_url,
        opts.client_id,
        opts.scopes.iter(),
        opts.redirect_uri,
        Some(redirect_sender),
    )?;
    let url = redirect_receiver.recv_timeout(Duration::from_secs(1))?;
    println!("{}", url);
    Ok(())
}

#[derive(StructOpt)]
struct Opts {
    #[structopt(long, short)]
    /// OAuth2 authorize URL, such as "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
    authorize_url: Url,

    #[structopt(long, short)]
    /// Application ID, usually a UUID string such as "5c9da3e4-3032-4906-a57b-10a675e39154"
    client_id: String,

    #[structopt(long = "scope", short, name = "scope")]
    /// Security scopes to request; consider "email", "openid", "profile"
    scopes: Vec<String>,

    #[structopt(long, short)]
    /// Where will the browser be redirected with the code or error result?
    /// Use a URN or a custom scheme such as 'mytool' in "mytool://authenticate/" to handle this internally.
    redirect_uri: Url,
}

fn authenticate(
    base_url: impl AsRef<str>,
    client_id: impl AsRef<str>,
    scopes: impl Iterator<Item = impl AsRef<str>>,
    redirect_uri: impl AsRef<str>,
    redirect_handler: Option<Sender<String>>,
) -> anyhow::Result<()> {
    let req = AuthorizationCodeRequest {
        client_id: client_id.as_ref().to_owned(),
        redirect_uri: redirect_uri.as_ref().to_owned(),
        response_mode: "query".to_owned(),
        response_type: "code".to_owned(),
        scope: scopes.fold(String::new(), |mut scopes, scope| {
            if !scopes.is_empty() {
                scopes.push_str(" ")
            }
            scopes.push_str(scope.as_ref());
            scopes
        }),
        state: None,
        prompt: None,
        login_hint: None,
        code_challenge: None,
        code_challenge_method: None,
    };

    let mut app = Application::new()?;
    let mut query = to_urlenc(&req)?;
    query.insert_str(0, "?");
    query.insert_str(0, base_url.as_ref());

    info!("OAuth query: {:?}", query);

    let attributes = Attributes {
        url: Some(query),
        title: String::from("Authenticate with OAuth authorization code flow"),
        always_on_top: true,
        fullscreen: true,
        ..Default::default()
    };

    let custom_protocol = if let Some(sender) = redirect_handler {
        let sender = sender.clone();
        match Url::parse(redirect_uri.as_ref()) {
            Err(e) => {
                info!(
                    "Redirect uri cannot be parsed => custom handler will not be used. {}",
                    e
                );
                None
            }
            Ok(u)
                if u.scheme().is_empty()
                    || ["http", "https"].contains(&u.scheme().to_lowercase().as_str()) =>
            {
                info!(
                    "Redirect uri has {:?} scheme => custom handler will not be used.",
                    u.scheme()
                );
                None
            }
            Ok(u) => Some(CustomProtocol {
                name: u.scheme().to_owned(),
                handler: Box::new(move |url| match sender.send(url.to_owned()) {
                    Ok(()) => Ok(
                        b"<html><body>OK!<script>window.rpc.notify('done')</script></body></html>"
                            .to_vec(),
                    ),
                    Err(e) => Err(wry::Error::SenderError(e)),
                }),
            }),
        }
    } else {
        None
    };

    let wrpc =
        Box::new(
            move |proxy: WindowProxy, request: RpcRequest| match request.method.as_str() {
                "send" => unimplemented!("send url {:?}", request.params),
                "done" => proxy
                    .close()
                    .ok()
                    .map(|_| RpcResponse::new_result(request.id, request.params)),
                other => unimplemented!("Unexpected method {:?}", other),
            },
        );

    app.add_window_with_configs(attributes, Some(wrpc), custom_protocol, None)?;
    app.run();
    Ok(())
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
struct AuthorizationCodeRequest {
    client_id: String,
    redirect_uri: String,
    response_type: String,
    response_mode: String,
    scope: String,
    state: Option<String>,
    prompt: Option<String>,
    code_challenge: Option<String>,
    code_challenge_method: Option<String>,
    login_hint: Option<String>,
}

fn to_urlenc<T>(form: &T) -> anyhow::Result<String>
where
    T: Serialize,
{
    errish(block_on(errish(Body::from_form(form))?.into_string()))
}
fn errish<T>(
    r: std::result::Result<T, http_types::Error>,
) -> std::result::Result<T, anyhow::Error> {
    r.map_err(|e| anyhow::anyhow!(e))
}

Android support

Bellow list is exploring list, not implementation. I'll start implementing once all are feasible.

  • Init scripts
  • IPC
  • custom protocol

Evaluate script is meaningless until we can provide some Android window (activity/fragment) library. See this comment for more info.

Support for setting the background color of the webview

Is your feature request related to a problem? Please describe.
There is no direct problem related to this feature request. But it would be good to have so that white flashes are not visible especially when your application is loading and you do not have a white background.

Describe the solution you'd like
Be able to set the background color of the webview

Describe alternatives you've considered

Would you assign yourself to implement this feature?

  • Yes
  • No

I wish I could help

Additional context

Examples fail to open on windows.

Describe the bug
On the current iteration of WRY, if you attempt to run any of the examples on windows it will fail. A transient window will pop up before it is quickly closed and a WebView2Error(Error { hresult: -2147024894 }) error is produced in the console.

Steps To Reproduce
Steps to reproduce the behavior:
On windows, call cargo run --example and use any example aside from GTK.

Expected behavior
The window should open properly.

Screenshots
If applicable, add screenshots to help explain your problem.

Platform and Versions (please complete the following information):
OS: Windows 10
Rustc: 1.50.0 (cb75ad5db 2021-02-10)

Would you assign yourself to resolve this bug?

  • Yes
  • No

Additional context
This wasn't a problem with the version of wry from about a week ago.

Checked the webview 2 library and it also is throwing all sorts of errors. 'Failed to Create WebView2 Environment - webview2 error, HRESULT 0x80070002' for example.

Start making WRY more DRY

If you are looking to get your hands dirty, this is a great issue to get started working with Rust and WRY.

In this PR, #82 there is some code repetition, and if you look closely in the codebase, there are some other cases of it. With regard to mime types (aka media types), there may be situations where we need to add others as time goes on, so having the list in one place makes it easier to update and know that we have feature parity.

Feel free to ask questions here in the issue, or on our discord server.

Step to install WebKitGTK

Current README only tells how to install WebKitGTK on arch Linux because my OS is arch (duh).
It would be better if we provide steps to install it on other distributions.

Event handling overhaul

Is your feature request related to a problem? Please describe.
Despite we now offer lots of methods in the application layer, it still lacks the ability like exposing Window or Webview types.

Describe the solution you'd like
We provide just one type of proxy and it just sends the events as winit does.
Users define their closure in the run method. We offer all the necessary params for them.
This should give users total control of the application behaviors.

Describe alternatives you've considered
Offer just another event manager. But I'm fear this will increase the interface complexity,

Would you assign yourself to implement this feature?
Yes

Failing doc-tests

Describe the bug
As discussed in #88, while running cargo test doctests seem to fail. This seems to be because one of two reasons:

  • The required module is not imported, and the code snippet is unable to compile
  • The API behind using a method has been changed. (For e.g. I came across an error where app.add_window API required 1 argument and was supplied 2 arguments, not sure why that's the case as it looks like the 2nd argument takes an Option<T> ๐Ÿคท )

NOTE: Inspite of using the no_run attribute, rust still tries to compile the examples. As mentioned by @wusyong we could use the ignore attribute instead, but there's a slight chance that the docs become out-of-date .

Steps To Reproduce
Run cargo test in the root.

Expected behavior
Doctests should ideally work to ensure https://docs.rs/wry/0.5.1/wry/ stays correct.

Platform and Versions (please complete the following information):
OS: macOS 11.2.1
Rustc: rustc 1.52.0-nightly (94736c434 2021-02-27)

Would you assign yourself to resolve this bug?

  • Yes
  • No

Windows User Data for bundled Application

Describe the bug
The application does not work when we bundle the application in Program Files as Webview2 tries to create the cache at <ExePath>.Webview2 as Program Files is protected the webview panic.

Steps To Reproduce

Put a compiled application inside Program Files and try to launch.

Expected behavior
Webview should open.

Screenshots
N/A

Platform and Versions (please complete the following information):
OS: Windows
Rustc: N/A

Would you assign yourself to resolve this bug?

  • Yes
  • No

Computationally Secure RANDOM

Describe the bug
https://github.com/tauri-apps/wry/blob/master/src/webview/mod.rs#L165

Random is NOT computationally secure. I am not sure what the implications are of it here. Are we are worried about it being hijacked? Since this is about message ID's I am actually worried about ID hijacking at the boundary. If you agree, we should use Crypto.random as shown below, or leverage some UUID (@lucasfernog - you wrote something like that for tauri... right?)

var array = new Uint32Array(10);
window.crypto.getRandomValues(array);

Would you assign yourself to resolve this bug?

  • MAYBE

Support for web APIs that require permissions

Is your feature request related to a problem? Please describe.
Currently (at least on Linux), doing any action that requires permission (such as geolocation) fails as the permission request is immediately denied.

Describe the solution you'd like
There should be a way to specify what permissions the app should have so they are automatically allowed.

Would you assign yourself to implement this feature?

  • Yes (at least for Linux and maybe windows)
  • No

Additional context
For linux: https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebView.html#WebKitWebView-permission-request

GLib-GIO-WARNING

I get Warning: Your application does not implement g_application_activate() and has no handlers connected to the 'activate' signal. It should do one of these.
For run

cargo run --example basic

For me this no look to no problem with example but with gio in wry.

All Shell output:

Your application does not implement g_application_activate() and has no handlers connected to the 'activate' signal.  It should do one of these.
http://localhost:3000/static/js/vendors~main.chunk.js:39300:18: CONSOLE LOG [HMR] Waiting for update signal from WDS...
1 seconds has passed.
http://localhost:3000/:1:12: CONSOLE LOG ใ‚ด
2 seconds has passed.
http://localhost:3000/:1:12: CONSOLE LOG ใ‚ด
3 seconds has passed.
http://localhost:3000/:1:12: CONSOLE LOG ใ‚ด
4 seconds has passed.
http://localhost:3000/:1:12: CONSOLE LOG ใ‚ด
5 seconds has passed.
http://localhost:3000/:1:12: CONSOLE LOG ใ‚ด
6 seconds has passed.
http://localhost:3000/:1:12: CONSOLE LOG ใ‚ด
7 seconds has passed.
http://localhost:3000/:1:12: CONSOLE LOG ใ‚ด

Custom logging (duplicate logs on Linux with debug builds)

Is your feature request related to a problem? Please describe.
Yes. Currently on Linux the call set_enable_write_console_messages_to_stdout() hinders a custom logging solution during development by duplicating log messages.

Describe the solution you'd like
Remove the call to set_enable_write_console_messages_to_stdout(); from what I can tell there is no equivalent for Windows/macOS so I think it is consistent if we remove this.

Describe alternatives you've considered
Not sure there is any alternative to prevent duplicate messages in debug builds on Linux as I don't think it's practical nor desirable to disable custom logging in that situation.

Would you assign yourself to implement this feature?

  • Yes

Additional context
I want console log messages to always be routed via my own logging facility regardless of build type and whether console is actually available. Here is an example of how I achieve this:

const embedded = typeof rpc !== 'undefined';

if (embedded) {
  const tee = false;

  const console_methods = {
    log: console.log,
    info: console.info,
    warn: console.warn,
    error: console.error,
  }

  console.log = function() {
    const args = Array.prototype.slice.call(arguments, 0);
    if (tee && typeof console_methods.log === 'function') {
      console_methods.log.apply(null, args);
    }
    return window.rpc.notify('console.log', ...args)
  }
  console.info = function() {
    const args = Array.prototype.slice.call(arguments, 0);
    if (tee && typeof console_methods.info === 'function') {
      console_methods.info.apply(null, args);
    }
    return window.rpc.notify('console.info', ...args);
  }
  console.warn = function() {
    const args = Array.prototype.slice.call(arguments, 0);
    if (tee && typeof console_methods.warn === 'function') {
      console_methods.warn.apply(null, args);
    }
    return window.rpc.notify('console.warn', ...args);
  }
  console.error = function() {
    const args = Array.prototype.slice.call(arguments, 0);
    if (tee && typeof console_methods.error === 'function') {
      console_methods.error.apply(null, args);
    }
    return window.rpc.notify('console.error', ...args);
  }
}

Currently if tee is true then it routes via the existing console functions and the duplicates will appear on Linux for debug builds. I would like to be enable tee and have no duplicates.

Extra default setting For WebKit in Linux

In my opinion will be good add some extra settings for linux version in InnerWebView like:

  • WebAudio, Cache for working app or Smooth.
  • I'm not sure that is correct way but testin on the youtube look fine.

platform/linux.rs->InnerWebView{.....

// == Enable webgl, webaudio, canvas features and others == //
        if let Some(settings) = WebViewExt::get_settings(&*webview) {
            settings.set_enable_webgl(true);
            settings.set_enable_webaudio(true);
            settings.set_enable_accelerated_2d_canvas(true);
            settings.set_javascript_can_access_clipboard(true);

            // == Cache For App == //
            settings.set_enable_offline_web_application_cache(true);
            settings.set_enable_page_cache(true);

            // == Smooth_scrooling == //
            settings.set_enable_smooth_scrolling(true);

            if debug {
                settings.set_enable_write_console_messages_to_stdout(true);
                settings.set_enable_developer_extras(true);
            }
        }

WebRTC support on Linux

I tested only on macOS.
It might already work on other platforms.

Is your feature request related to a problem? Please describe.
I want to use WebRTC

Describe the solution you'd like
I don't have any preference.
I just want to use WebRTC.

Describe alternatives you've considered

  • With system webbrowser (doesn't work on macOS)
    Notification.requestPermission() // => undefined
    navigator.mediaDevices // => undefined
  • Using another process
    I don't know any library to use WebRTC with VideoUI on desktop ๐Ÿ˜ญ

Additional context

It seems, the cause of this issue is this https://forums.developer.apple.com/thread/88052

And, the following might solve this issue on macOS (not tested).
https://bugs.webkit.org/show_bug.cgi?id=208667#c17


There are similar issues in other repositories.

Delay when processing javascript messages sent from the backend on Linux/GTK

Describe the bug
This bug occurs on the latest dev branch. I'm sending in a stream of evaluate_script commands into Wry as the backend listens to events from an external source. On the Winit backend this works perfectly, but on GTK these events are delayed in making it to the browser unless the user is actively engaged with the browser, such as clicking on something or moving their mouse. This happens because of the call to gtk::main_iteration() on the main loop used by src/application/gtkrs.rs. gtk::main_iteration() is a blocking call, so once one iteration of the loop completes, no more messages sent to self.event_loop_proxy_rx will be processed until GTK thinks it needs to run the next iteration. If the user isn't actively using the browser, it appears GTK doesn't think it needs to update very often and only runs the next loop roughly once every second. As a result, every javascript callback sent into the queue gets batch executed roughly once per second instead of in real time.

Steps To Reproduce
I've created an example here that demonstrates this behavior. On Windows it behaves as expected, but on Linux it demonstrates the processing delay. The backend sends a counter increment to the UI every 500 milliseconds, but the UI only updates every second, so you should only see every other number displayed on the UI unless you focus on the window and start moving your mouse, then the UI should begin to update as expected. If you change the interval to something smaller, the batch delay becomes more apparent.

Expected behavior
I should be able to push events to the UI from the backend without user intervention and see the UI update without major processing delay.

Platform and Versions (please complete the following information):
OS: Arch Linux
Rustc: 1.51

Would you assign yourself to resolve this bug?

  • Yes
  • No

If I swap the call to gtk::main_iteration() with the nonblocking version gtk::main_iteration_do(false), the code begins to process messages quickly and behave as expected, but now the entire loop is nonblocking and eats up a large amount of CPU, which is not great. The most obvious solution to me is to have the event loop for self.event_loop_proxy_rx run in a separate thread so it can process messages without waiting for gtk's event loop, however it appears that the gtk objects used in self.webviews are not threadsafe, so the thread that handles the event loop will also need complete ownership of the webviews.

I'd be happy to take a stab at this, but I'd like some input on my thoughts above as well as any other potential solutions first. Thanks!

Consider bi-directional RPC communication

Is your feature request related to a problem? Please describe.
Not a problem but I think the ergonomics of the Callback API could be improved or potentially replaced with a higher-level RPC API.

Describe the solution you'd like
As I call wry callbacks from Javascript I don't want to define a new Callback for every function and I want to transparently return data to my Javascript code via Promises. I am currently writing a wrapper on top of the Callback API that achieves this using JSON-RPC but it feels icky because wry is already using JSON-RPC under the hood so I am effectively embedding a JSON-RPC message in the parameter of a JSON-RPC message.

I would like to propose a solution that uses a Service trait instead of a Callback function and RPC messages are handled transparently using a simple embedded Javascript client (window.rpc maybe?). The Javascript client would expose two public functions call() to call out to a service and receive a reply (via a Promise) and send() which would not include the id and therefore not receive a response from the Service implementation(s).

Describe alternatives you've considered
I have a working prototype but I think this is such a common requirement that it should be part of wry.

Would you assign yourself to implement this feature?

  • Yes (Linux only)
  • No

Additional context
The existing Rust JSON-RPC libraries are not a good fit for various reasons so I put together this json-rpc2 crate that I am using for the service implementation.

Happy to share more if this interests you.

Security of message passing

Is your feature request related to a problem? Please describe.
In #66 we began discussions about how to enhance the security model around message passing between wry and the webview. Specifically in the case of the RPC approach, we need to figure out a lightweight method for message authentication and potential encryption. Generally speaking it would be wise to NEVER use untrusted javascript, but we know that people will always do so. Tailor-made attack endpoints, if they know they are being consumed by a tauri / wry app, can leverage this knowledge in order to use / abuse the backend API.

Describe the solution you'd like
I would like to have a discussion about methods by which such a channel can use a system like noise protocol to generate ephemeral keys at startup (between trusted JS and Rust) in order for JS to send Rust a message. Since Rust OWNS the webview, it might seem that we do not need Rust to authenticate itself, but this is not the case. An evil thread might try to send a message to the webview, and this is why having a secure transport layer is important.

A lightweight solution (that will admittedly have overhead) might be to use one-time-keys / salts that our trusted rust generates each time a message has been received and places in the WEBVIEW JS domain for the next message. This key could be used with some cryptolib to encrypt the message coming from the webview.

Now, the problem still remains, how do we protect an evil iFrame (or worse, some remote script from a corrupt CDN) from knowing / discovering this key, using it "appropriately", and then using our RPC / window.evaluate ?

Another heavyhanded option might be to actually inspect the javascript caller from rust and then "acceptlist it" or run it through some kind of "DOMpurifier", although to be fair I have no idea if this would really work.

Can we inspect and "approve" remote JS before letting it sexecute?

Overwriting and freezing Object primitives seems similarly heavyhanded, but if I were a totally paranoid about security, I would definitely consider this.

Describe alternatives you've considered
We looked at some approaches here: #66 (comment)

At the moment we have several mitigations in place:

  1. CSP injected by tauri can prevent remote execution (not always an option)
  2. Recommend that iframes are sandboxed (does not treat e.g. <SVG> or <OBJECT>attack vectors)
  3. Randomize the function handler names (can be inspected at runtime and brute forced)

Would you assign yourself to implement this feature?

  • Yes with regard to the security review

Add examples

We want several examples to showcase how wry can do.

This can also help us writing mcve when there's a bug report issue.

PRs are welcome!

Webpage screenshots

Is your feature request related to a problem? Please describe.
I think it would be useful to be able to capture screenshots of the rendered document/webpage, for example to programmatically screenshot web servers of as part of automated testing.

Stretch goal: potentially build a viable replacement for phantomjs, but that's probably not in scope for this issue.

Describe the solution you'd like
Ideally, some way to read the rendered framebuffer into a Vec of pixels so that it can be processed/encoded as PNG/etc.

Describe alternatives you've considered
In theory it should be possible to achieve this by injecting a copy of html2canvas into the page and then somehow getting the captured image back out of the webview, but some method for directly reading the rendered page would be cleaner (and more performant).

Would you assign yourself to implement this feature?

  • Yes - if it turns out to be relatively simple
  • No - if it requires major changes to Wry as I'm not familiar with the codebase

Additional context
Add any other context or screenshots about the feature request here.

macos issue when using `run_return` on the winit event loop - [webview is not created]

Describe the bug
When I create a new webview inside a winit window on macOS using the run_return loop the webview didn't get created. I need to resize the window manually to get it to work.

With the tests I've made, the webview didn't get created at all as I see as the right-click is not enabled, so it seems to be only the winit window.

Steps To Reproduce
I created a reproducible example available here:
โ†’ https://github.com/lemarier/wry_macos_bug

Expected behavior
Webview should be created and initialized on the first draw.

Screenshots
sample gif

Platform and Versions (please complete the following information):
OS: macOS Big Sur 11.2.3
Rustc: 1.51.0 (2fd73fabe 2021-03-23)

Would you assign yourself to resolve this bug?

  • Yes
  • No

Release on NARC

I believe we didn't handle NARC on objc runtime yet.
We should add autorelease to those.

Application layer

Starting with v0.3, every compenent to build a web app window is here. There's nothing to prevent from you building your own window anymore!

That being said, users still need to handle window creation and event loop themselves. We probably need a application layer provides methods that are idiomatic and easy to use. It will lose some fine grained configuration, but I hope it is sensible default for majority of use cases. My current thought is basically two method add_window and run like below:

Application::new()
    .add_window(config) // config is a struct that can be built with default pattern
    .run() // This will block the thread and maybe consume itself

Feedbacks are welcome!

Option do disable the default context menu

When packaging a default webapp, then the context menu (at least on macOS) has 2 entries:

  • reload
  • back

I wonder if there is an option to disable the context menu by configuration?
Of course, I can implement that in JavaScript but I wonder if that is not like a common case, where people want to default to no context menu actions because their webapp does not have any own ones.

Avoid deprecated functions in webkit2gtk

After update webkit2gtk features from v2_8 to v2_28 I got warning that get_value() and get_global_contex() is Deprecated.
I finded on webview/webview@f78d582
that the newer version get_value() is get_js_value() and how I understand get_global_contex() is complety removed(??).
I tryed update this alone (...) but i am fallen.

Code part in the file "platform/linux.rs/InnerWebView" looks now:

manager.connect_script_message_received(move |_m, msg| {
            if let Some(js) = msg.get_js_value() {
                let js = msg.to_string(&js);
                let v: RPC = serde_json::from_str(&js).unwrap();
                let mut hashmap = CALLBACKS.lock().unwrap();
                let (f, d) = hashmap.get_mut(&(window_id, v.method)).unwrap();
                let status = f(d, v.id, v.params);

                let js = match status {
                    0 => {
                        format!(
                            r#"window._rpc[{}].resolve("RPC call success"); window._rpc[{}] = undefined"#,
                            v.id, v.id
                        )
                    }
                    _ => {
                        format!(
                            r#"window._rpc[{}].reject("RPC call fail"); window._rpc[{}] = undefined"#,
                            v.id, v.id
                        )
                    }
                };
                let cancellable: Option<&Cancellable> = None;
                wv.run_javascript(&js, cancellable, |_| ());
            }

I have tried a few things and ultimately this is the result with only one error. The to_string () function exists but I have too little Rust skill to properly send data to it. Or something else ? In my opinion this maybe be important for near future gtk4-rs and bilding webkit2gtk. Ofcourse Im not sure. Can anyone with more skill and experience in Rust, webkit2gtk help me?

error[E0599]: no method named `to_string` found for reference `&JavascriptResult` in the current scope
  --> src/platform/linux.rs:34:30
   |
34 |                   let js = msg.to_string(&js);
   |                                ^^^^^^^^^ method not found in `&JavascriptResult`
   | 
  ::: /home/euuki/.cargo/registry/src/github.com-1ecc6299db9ec823/webkit2gtk-0.11.0/src/auto/javascript_result.rs:9:1
   |
9  | / glib_wrapper! {
10 | |     #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
11 | |     pub struct JavascriptResult(Shared<webkit2_sys::WebKitJavascriptResult>);
12 | |
...  |
17 | |     }
18 | | }
   | | -
   | | |
   | |_doesn't satisfy `JavascriptResult: ToString`
   |   doesn't satisfy `JavascriptResult: std::fmt::Display`
   |
   = note: the method `to_string` exists but the following trait bounds were not satisfied:
           `JavascriptResult: std::fmt::Display`
           which is required by `JavascriptResult: ToString`
           `&JavascriptResult: std::fmt::Display`
           which is required by `&JavascriptResult: ToString`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.
error: could not compile `wry

Webview2 binding crate

We switched back to window-rs in dev branch now which is pretty great.
This means we are also going to publish webview2 binding crate.
Some refactoring and documentation might be required before we actually publishing.

Secure Context

Having the webview content be considered "secure" is something that users may want to enable if they want to use geolocation, camera, high-resolution timers etc. Can we have a config that allows them to turn this on for all platforms?

Background:

A secure context is determined by browsers using file://, localhost, https:// - since code injection is none of these, we don't have a secure context...

Navigate html file

The current navigation method only supports URL as input.
Let's make it able to handle HTML.
Or maybe we could add another method for this.

C API

One of the goals for this project is to contribute back to webview/webview community.
The application module seems ergonomic enough to add a C FFI wrapper to it.
We could add another crate as cdylib for this.

Getting a real path of file type input

Is your feature request related to a problem? Please describe.
I am trying to get a the real path of file selected in <input type="file" id="myFile"> but I dunno how to.

I tried document.getElementById("myFile").files which gives array of File which contains following information.

lastModified: 1597767566000
name: "filename.py"
size: 859
type: "text/x-python"
webkitRelativePath: ""

Describe the solution you'd like
Electron adds a path property in File object. I don't know how exactly it is happening and will it be possible in Tauri
but this seems to be one possible solution to me.
https://github.com/electron/electron/blob/master/docs/api/file-object.md

Custom protocol

Add support for custom protocol like tauri://. It's possible on gtk framework:

WebContextExt::register_uri_scheme(&context, &"tauri", |arg| {
            let file_path = arg
                .get_uri()
                .unwrap()
                .as_str()
                .replace("tauri://", "dist/spa/")
                // Somehow other assets get index.html in their path
                .replace("index.html/", "/");
            println!("Loading {}", file_path);
            let mime = mime_guess::from_path(&file_path)
                .first()
                .unwrap()
                .to_string();
            let file = File::open(file_path).expect("Can't find");
            let length = file.metadata().unwrap().len() as i64;
            let input = gio::ReadInputStream::new(file);
            arg.finish(&input, length, Some(&mime))
        });

We need to know if it's possible on macOS and Windows. Current candidate could be the following:
https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/2875766-seturlschemehandler
https://docs.microsoft.com/en-us/uwp/api/windows.web.iuritostreamresolver.uritostreamasync?view=winrt-19041#Windows_Web_IUriToStreamResolver_UriToStreamAsync_Windows_Foundation_Uri_

Window getters

Is your feature request related to a problem? Please describe.
Add getters on WindowProxy

Describe alternatives you've considered
Keep track of an Attributes struct and update it whenever a setter method is called but that won't work properly for maximized, minimized and more.

Would you assign yourself to implement this feature?

  • Yes
  • No

Additional context
Most of the getters can be implemented for gkt-backend but not winit-backend
I made a table to list what is possible and what is not

getters winit gtk
resizable no yes
title no yes
maximized yes yes
minimized no maybe(2)
visible no yes
decoration no yes
always_on_top no maybe(2)
width, height yes yes
min/max width/height no maybe(2)
x , y yes yes
fullscreen yes maybe(2)
skip_taskbar no yes
url(3)
icon(4)
transparent(4)
initialization_scripts(4)
  1. gtk docs are confusing and I am not sure if the getter is possible.
  2. url is possible I guess but not sure if it's worth it.
  3. I think transparent, icon, initialization_scripts, and skip_taskbar don't need getters imo.

Resize is lagging.

it would be good to have
min-width, max-width <-- this 2 is good for PWA, if user resized it too small, it would become ugly.
maximized <-- this would be maximized on first load.

another thing would be, try delay webview resize when the host window resize, like how all the browser did, this would make sure PWA reflective size won't response immediately... current resize is seriously lagging for PWA but it was smooth in browser.

Originally posted by @thienpow in tauri-apps/tauri#539 (comment)

Prepare repo for production

Obviously we need to have a number of things added to this repository:

  • CI: clippy, fmt, licensecheck
  • CI: matrix tests
  • CI: covector with appropriate tokens
  • CI: Dependabot
  • CI: Hook to update tauri.studio documentation
  • ORG: issue template
  • ORG: PR template
  • ORG: security policy
  • ORG: community guidelines
  • DEV: contribution info

data:text/html shows in html output

Describe the bug
data:text/html prefix shows in html output

Steps To Reproduce
Steps to reproduce the behavior:

use wry::Result;
use wry::{Application, Attributes};

fn main() -> Result<()> {
    let mut app = Application::new()?;

    let attributes = Attributes {
        url: Some(
            r#"data:text/html,
            <!doctype html>
            <html>
              <body>Hello</body>
            </html>"#
                .to_string(),
        ),
        ..Default::default()
    };

    app.add_window(attributes, None)?;
    app.run();
    Ok(())
}

Expected behavior
Should not output data:text/html

Platform and Versions (please complete the following information):
OS: Macos
Rustc: 1.50

Would you assign yourself to resolve this bug?

  • Yes (if needed)
  • No

Additional context
Something like this could help

// macos.rs, line 152:
// Navigation
if let Some(url) = url {
    if url.cannot_be_a_base() {
        let url_str = url.as_str();
        let url_str = if url_str.starts_with("data:text/html,") {
            &url_str[15..]
        } else {
            url_str
        };
        w.navigate_to_string(url_str);
        // w.navigate_to_string(url.as_str());
    } else {
        w.navigate(url.as_str());
    }
}

cargo test fails on macos due to gtk.rs example

Describe the bug
When running cargo test on macos, the build fails as it seems to compile gtk.rs. From my limited knowledge, I believe we probably need a cfg flag of some sort to ignore compilation of this example on macos.

  |
3 | use cairo::*;
  |     ^^^^^ use of undeclared crate or module `cairo`

error[E0432]: unresolved import `gtk`
 --> examples/gtk.rs:4:5
  |
4 | use gtk::*;
  |     ^^^ use of undeclared crate or module `gtk`

error[E0433]: failed to resolve: use of undeclared crate or module `gtk`
 --> examples/gtk.rs:7:5
  |
7 |     gtk::init()?;
  |     ^^^ use of undeclared crate or module `gtk`

error[E0433]: failed to resolve: use of undeclared type `WindowType`
 --> examples/gtk.rs:8:30
  |
8 |     let window = Window::new(WindowType::Toplevel);
  |                              ^^^^^^^^^^ use of undeclared type `WindowType`

error[E0433]: failed to resolve: use of undeclared crate or module `gtk`
  --> examples/gtk.rs:31:5
   |
31 |     gtk::main();
   |     ^^^ use of undeclared crate or module `gtk`

error[E0433]: failed to resolve: use of undeclared type `Window`
 --> examples/gtk.rs:8:18
  |
8 |     let window = Window::new(WindowType::Toplevel);
  |                  ^^^^^^ not found in this scope
  |
help: consider importing one of these items
  |
1 | use winit::window::Window;
  |
1 | use wry::platform::window::Window;

Steps To Reproduce
Steps to reproduce the behavior:

  • Run cargo test in the root.

Expected behavior
Tests should work.

Platform and Versions (please complete the following information):
OS: macOS 11.2.1
Rustc: rustc 1.52.0-nightly (94736c434 2021-02-27)

Would you assign yourself to resolve this bug?

  • Yes
  • No

Resizing detection

Webview on macOS is set as 800x600 by default for now. We should determine it based on the size of the window.

We also need to expose a method to let users manually call when to resize.

wry panics if non-files are dropped on it

Describe the bug
wry panics if non-files are dropped on it

Steps To Reproduce

use wry::{Application, Attributes, Result};

fn main() -> Result<()> {
  let mut app = Application::new()?;

  let attributes = Attributes {
    url: Some("https://www.google.com/".to_string()),
    title: String::from("App"),
    transparent: true,
    ..Default::default()
  };

  let w = app.add_window_with_configs(attributes, None, None, Some(Box::new(|e| {
    println!("{:?}", e);
    true
  })))?;
  
  app.run();
  Ok(())
}
  • cargo run
  • drop a bookmark from chrome on the window

Expected behavior
It does not crash.

Actual Behavior

finished dev [unoptimized + debuginfo] target(s) in 2.13s
     Running `target\debug\wry-demo-2.exe`
thread 'main' panicked at 'Error occured while processing dropped/hovered item: item is not a file.', C:\Users\henni\.cargo\git\checkouts\wry-0ba07c7b81cd1a87\d2a2a9f\src\webview\win\file_drop.rs:295:7
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fatal runtime error: Rust panics must be rethrown
error: process didn't exit successfully: `target\debug\wry-demo-2.exe` (exit code: 0xc0000409, STATUS_STACK_BUFFER_OVERRUN)

Platform and Versions (please complete the following information):
OS: Windows 10
Rustc: rustc 1.53.0-nightly (07e0e2ec2 2021-03-24)

Would you assign yourself to resolve this bug?

  • Yes
  • No

Add an option to keep the event loop alive even if the last window is closed

WRY is amazing! Thanks for creating it - I think it has a very bright future ahead!

Is your feature request related to a problem? Please describe.
I'd like to create an RPC server that spawns windows.
However, the event loop is terminated when the last window is closed, even though the RPC server is still active and might spawn a new window in the future.

Describe the solution you'd like
Please make it configurable if the event loop should terminate when last window closes.

Describe alternatives you've considered
Maybe the event loop could be put into an outer loop, so that it is restarted whenever it terminates. However, this invalidates the app proxy and is a very hacky solution.

Would you assign yourself to implement this feature?

  • Yes
  • No

Directories and files such as .gitignore are interpreted as HTML MIME type

Describe the bug

These lines:

let suffix = uri.split(".").last();
...
// Assume HTML when a TLD is found for eg. `wry:://tauri.studio` | `wry://hello.com`
Some(_) => Self::HTML,

In src/mimetype.rs cause files such as .gitignore to be interpreted as HTML files.

Steps To Reproduce

println!("{:?}", crate::mimetype::MimeType::parse_from_uri("C:\\Users\\billy\\.gitignore_global"));

Expected behavior

I believe it should return MimeType::OCTETSTREAM

Screenshots
If applicable, add screenshots to help explain your problem.

image

Platform and Versions (please complete the following information):
OS: Windows
Rustc: rustc 1.48.0 (7eac88abb 2020-11-16)

Would you assign yourself to resolve this bug?

  • Yes
  • No

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.