Skip to content

Commit

Permalink
feat: add headers when loading URLs, closes #816 (#826)
Browse files Browse the repository at this point in the history
* feat: add `load_url_with_headers`, close #816

* windows

* android

* macOS

* fix macos header usage

* Restore examples/custom_titlebar.rs

* remove duplication

* add kotlin code

* cleanup duplicated code

* refactor signature

* fix examples

* Revert "fix examples"

This reverts commit de22b48.

* Revert "refactor signature"

This reverts commit c9e1a61.

* rename to `with_url_and_headers`

Co-authored-by: Lucas Nogueira <[email protected]>
  • Loading branch information
amrbashir and lucasfernog authored Dec 31, 2022
1 parent 14a0ee3 commit 8ae93b9
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 63 deletions.
5 changes: 5 additions & 0 deletions .changes/load_url_with_headers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wry": "patch"
---

Add `WebViewBuilder::with_headers` and `WebView::load_url_with_headers` to navigate to urls with headers.
19 changes: 2 additions & 17 deletions src/webview/android/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use tao::platform::android::ndk_glue::jni::{
JNIEnv,
};

use super::{IPC, REQUEST_HANDLER, TITLE_CHANGE_HANDLER};
use super::{create_headers_map, IPC, REQUEST_HANDLER, TITLE_CHANGE_HANDLER};

fn handle_request(env: JNIEnv, request: JObject) -> Result<jobject, JniError> {
let mut request_builder = Request::builder();
Expand Down Expand Up @@ -105,22 +105,7 @@ fn handle_request(env: JNIEnv, request: JObject) -> Result<jobject, JniError> {
(JObject::null().into(), JObject::null().into())
};

let hashmap = env.find_class("java/util/HashMap")?;
let response_headers = env.new_object(hashmap, "()V", &[])?;
for (key, value) in response.headers().iter() {
env.call_method(
response_headers,
"put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
&[
env.new_string(key.as_str())?.into(),
// TODO can we handle this better?
env
.new_string(String::from_utf8_lossy(value.as_bytes()))?
.into(),
],
)?;
}
let response_headers = create_headers_map(&env, response.headers())?;

let bytes = response.body();

Expand Down
7 changes: 7 additions & 0 deletions src/webview/android/kotlin/RustWebView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.annotation.SuppressLint
import android.webkit.*
import android.content.Context
import android.os.Build
import kotlin.collections.Map

class RustWebView(context: Context): WebView(context) {
init {
Expand All @@ -26,5 +27,11 @@ class RustWebView(context: Context): WebView(context) {
}
}

fun loadUrlMainThread(url: String, additionalHttpHeaders: Map<String, String>) {
post {
super.loadUrl(url, additionalHttpHeaders)
}
}

{{class-extension}}
}
52 changes: 35 additions & 17 deletions src/webview/android/main_pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ use std::os::unix::prelude::*;
use tao::platform::android::ndk_glue::{
jni::{
errors::Error as JniError,
objects::{GlobalRef, JObject},
objects::{GlobalRef, JObject, JString},
JNIEnv,
},
PACKAGE,
};

use super::find_my_class;
use super::{create_headers_map, find_my_class};

static CHANNEL: Lazy<(Sender<WebViewMessage>, Receiver<WebViewMessage>)> = Lazy::new(|| bounded(8));
pub static MAIN_PIPE: Lazy<[RawFd; 2]> = Lazy::new(|| {
Expand Down Expand Up @@ -50,6 +50,7 @@ impl MainPipe<'_> {
devtools,
transparent,
background_color,
headers,
} = attrs;
// Create webview
let rust_webview_class = find_my_class(
Expand All @@ -65,12 +66,7 @@ impl MainPipe<'_> {

// Load URL
if let Ok(url) = env.new_string(url) {
env.call_method(
webview,
"loadUrlMainThread",
"(Ljava/lang/String;)V",
&[url.into()],
)?;
load_url(env, webview, url, headers, true)?;
}

// Enable devtools
Expand Down Expand Up @@ -178,15 +174,10 @@ impl MainPipe<'_> {
f(env, activity, webview.as_obj());
}
}
WebViewMessage::LoadUrl(url) => {
WebViewMessage::LoadUrl(url, headers) => {
if let Some(webview) = &self.webview {
let s = env.new_string(url)?;
env.call_method(
webview.as_obj(),
"loadUrl",
"(Ljava/lang/String;)V",
&[s.into()],
)?;
let url = env.new_string(url)?;
load_url(env, webview.as_obj(), url, headers, false)?;
}
}
}
Expand All @@ -195,6 +186,32 @@ impl MainPipe<'_> {
}
}

fn load_url<'a>(
env: JNIEnv<'a>,
webview: JObject<'a>,
url: JString<'a>,
headers: Option<http::HeaderMap>,
main_thread: bool,
) -> Result<(), JniError> {
let function = if main_thread {
"loadUrlMainThread"
} else {
"loadUrl"
};
if let Some(headers) = headers {
let headers_map = create_headers_map(&env, &headers)?;
env.call_method(
webview,
function,
"(Ljava/lang/String;Ljava/util/Map;)V",
&[url.into(), headers_map.into()],
)?;
} else {
env.call_method(webview, function, "(Ljava/lang/String;)V", &[url.into()])?;
}
Ok(())
}

fn set_background_color<'a>(
env: JNIEnv<'a>,
webview: JObject<'a>,
Expand Down Expand Up @@ -223,7 +240,7 @@ pub enum WebViewMessage {
GetWebViewVersion(Sender<Result<String, Error>>),
GetUrl(Sender<String>),
Jni(Box<dyn FnOnce(JNIEnv, JObject, JObject) + Send>),
LoadUrl(String),
LoadUrl(String, Option<http::HeaderMap>),
}

#[derive(Debug)]
Expand All @@ -232,4 +249,5 @@ pub struct CreateWebViewAttributes {
pub devtools: bool,
pub transparent: bool,
pub background_color: Option<RGBA>,
pub headers: Option<http::HeaderMap>,
}
24 changes: 22 additions & 2 deletions src/webview/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use tao::platform::android::ndk_glue::{
JNIEnv,
},
ndk::looper::{FdEvent, ForeignLooper},
PACKAGE,
JMap, PACKAGE,
};
use url::Url;

Expand Down Expand Up @@ -155,6 +155,7 @@ impl InnerWebView {
custom_protocols,
background_color,
transparent,
headers,
..
} = attributes;

Expand All @@ -173,6 +174,7 @@ impl InnerWebView {
devtools,
background_color,
transparent,
headers,
}));
}

Expand Down Expand Up @@ -282,7 +284,11 @@ impl InnerWebView {
}

pub fn load_url(&self, url: &str) {
MainPipe::send(WebViewMessage::LoadUrl(url.to_string()));
MainPipe::send(WebViewMessage::LoadUrl(url.to_string(), None));
}

pub fn load_url_with_headers(&self, url: &str, headers: http::HeaderMap) {
MainPipe::send(WebViewMessage::LoadUrl(url.to_string(), Some(headers)));
}
}

Expand Down Expand Up @@ -342,3 +348,17 @@ fn find_my_class<'a>(
.l()?;
Ok(my_class.into())
}

fn create_headers_map<'a, 'b>(
env: &'a JNIEnv,
headers: &http::HeaderMap,
) -> std::result::Result<JMap<'a, 'b>, JniError> {
let obj = env.new_object("java/util/HashMap", "()V", &[])?;
let headers_map = JMap::from_env(&env, obj)?;
for (name, value) in headers.iter() {
let key = env.new_string(name)?;
let value = env.new_string(value.to_str().unwrap_or_default())?;
headers_map.put(key.into(), value.into())?;
}
Ok(headers_map)
}
21 changes: 18 additions & 3 deletions src/webview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller;
#[cfg(target_os = "windows")]
use windows::{Win32::Foundation::HWND, Win32::UI::WindowsAndMessaging::DestroyWindow};

use std::borrow::Cow;
use std::{path::PathBuf, rc::Rc};
use std::{borrow::Cow, path::PathBuf, rc::Rc};

pub use url::Url;

Expand Down Expand Up @@ -83,6 +82,8 @@ pub struct WebViewAttributes {
pub background_color: Option<RGBA>,
/// Whether load the provided URL to [`WebView`].
pub url: Option<Url>,
/// Headers used when loading the requested `url`.
pub headers: Option<http::HeaderMap>,
/// Whether page zooming by hotkeys is enabled
///
/// ## Platform-specific
Expand Down Expand Up @@ -237,6 +238,7 @@ impl Default for WebViewAttributes {
transparent: false,
background_color: None,
url: None,
headers: None,
html: None,
initialization_scripts: vec![],
custom_protocols: vec![],
Expand Down Expand Up @@ -437,15 +439,24 @@ impl<'a> WebViewBuilder<'a> {
self
}

/// Load the provided URL with given headers when the builder calling [`WebViewBuilder::build`] to create the
/// [`WebView`]. The provided URL must be valid.
pub fn with_url_and_headers(mut self, url: &str, headers: http::HeaderMap) -> Result<Self> {
self.webview.url = Some(url.parse()?);
self.webview.headers = Some(headers);
Ok(self)
}

/// Load the provided URL when the builder calling [`WebViewBuilder::build`] to create the
/// [`WebView`]. The provided URL must be valid.
pub fn with_url(mut self, url: &str) -> Result<Self> {
self.webview.url = Some(Url::parse(url)?);
self.webview.headers = None;
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.
/// [`WebView`]. This will be ignored if `url` is provided.
///
/// # Warning
/// The Page loaded from html string will have different Origin on different platforms. And
Expand Down Expand Up @@ -798,6 +809,10 @@ impl WebView {
pub fn load_url(&self, url: &str) {
self.webview.load_url(url)
}

pub fn load_url_with_headers(&self, url: &str, headers: http::HeaderMap) {
self.webview.load_url_with_headers(url, headers)
}
}

/// An event enumeration sent to [`FileDropHandler`].
Expand Down
24 changes: 19 additions & 5 deletions src/webview/webkitgtk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,16 @@ use glib::signal::Inhibit;
use gtk::prelude::*;
#[cfg(any(debug_assertions, feature = "devtools"))]
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::{
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher},
rc::Rc,
sync::Mutex,
sync::{Arc, Mutex},
};
use url::Url;
use webkit2gtk::{
traits::*, LoadEvent, NavigationPolicyDecision, PolicyDecisionType, UserContentInjectedFrames,
UserScript, UserScriptInjectionTime, WebView, WebViewBuilder,
traits::*, LoadEvent, NavigationPolicyDecision, PolicyDecisionType, URIRequest,
UserContentInjectedFrames, UserScript, UserScriptInjectionTime, WebView, WebViewBuilder,
};
use webkit2gtk_sys::{
webkit_get_major_version, webkit_get_micro_version, webkit_get_minor_version,
Expand Down Expand Up @@ -361,7 +360,7 @@ impl InnerWebView {

// Navigation
if let Some(url) = attributes.url {
web_context.queue_load_uri(Rc::clone(&w.webview), url);
web_context.queue_load_uri(Rc::clone(&w.webview), url, attributes.headers);
web_context.flush_queue_loader();
} else if let Some(html) = attributes.html {
w.webview.load_html(&html, Some("http://localhost"));
Expand Down Expand Up @@ -461,6 +460,21 @@ impl InnerWebView {
pub fn load_url(&self, url: &str) {
self.webview.load_uri(url)
}

pub fn load_url_with_headers(&self, url: &str, headers: http::HeaderMap) {
let req = URIRequest::builder().uri(url).build();

if let Some(ref mut req_headers) = req.http_headers() {
for (header, value) in headers.iter() {
req_headers.append(
header.to_string().as_str(),
value.to_str().unwrap_or_default(),
);
}
}

self.webview.load_request(&req);
}
}

pub fn platform_webview_version() -> Result<String> {
Expand Down
Loading

0 comments on commit 8ae93b9

Please sign in to comment.