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: add tracing for evaluate_script, ipc_handler and custom_protocols #1133

Merged
merged 7 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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
5 changes: 5 additions & 0 deletions .changes/tracing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wry": patch
---

Added tracing spans for `evaluate_script`, `ipc_handler` and `custom_protocols` behind the `tracing` feature flag.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,15 @@ os-webview = [
"x11-dl",
"gdkx11"
]
tracing = [ "dep:tracing" ]

[build-dependencies]
cfg_aliases = "0.1"

[dependencies]
libc = "0.2"
log = "0.4"
tracing = { version = "0.1", optional = true }
once_cell = "1"
serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1.0"
Expand Down
44 changes: 40 additions & 4 deletions src/android/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ use jni::errors::Result as JniResult;
pub use jni::{
self,
objects::{GlobalRef, JClass, JMap, JObject, JString},
sys::{jboolean, jobject, jstring},
sys::{jboolean, jint, jobject, jstring},
JNIEnv,
};
pub use ndk;

use super::{
ASSET_LOADER_DOMAIN, IPC, ON_LOAD_HANDLER, REQUEST_HANDLER, TITLE_CHANGE_HANDLER,
ASSET_LOADER_DOMAIN, EVAL_CALLBACKS, IPC, ON_LOAD_HANDLER, REQUEST_HANDLER, TITLE_CHANGE_HANDLER,
URL_LOADING_OVERRIDE, WITH_ASSET_LOADER,
};

Expand Down Expand Up @@ -72,6 +72,7 @@ macro_rules! android_binding {
[JString],
jboolean
);
android_fn!($domain, $package, RustWebView, onEval, [jint, JString]);
android_fn!(
$domain,
$package,
Expand Down Expand Up @@ -103,6 +104,10 @@ fn handle_request(
is_document_start_script_enabled: jboolean,
) -> JniResult<jobject> {
if let Some(handler) = REQUEST_HANDLER.get() {
#[cfg(feature = "tracing")]
let span =
tracing::info_span!("wry::custom_protocol::handle", uri = tracing::field::Empty).entered();

let mut request_builder = Request::builder();

let uri = env
Expand All @@ -112,7 +117,12 @@ fn handle_request(
.call_method(&uri, "toString", "()Ljava/lang/String;", &[])?
.l()?
.into();
request_builder = request_builder.uri(&env.get_string(&url)?.to_string_lossy().to_string());
let url = env.get_string(&url)?.to_string_lossy().to_string();

#[cfg(feature = "tracing")]
span.record("uri", &url);

request_builder = request_builder.uri(&url);

let method = env
.call_method(&request, "getMethod", "()Ljava/lang/String;", &[])?
Expand Down Expand Up @@ -152,7 +162,11 @@ fn handle_request(
}
};

let response = (handler.handler)(final_request, is_document_start_script_enabled != 0);
let response = {
#[cfg(feature = "tracing")]
let _span = tracing::info_span!("wry::custom_protocol::call_handler").entered();
(handler.handler)(final_request, is_document_start_script_enabled != 0)
};
if let Some(response) = response {
let status = response.status();
let status_code = status.as_u16() as i32;
Expand Down Expand Up @@ -267,9 +281,31 @@ pub unsafe fn shouldOverride(mut env: JNIEnv, _: JClass, url: JString) -> jboole
.into()
}

#[allow(non_snake_case)]
pub unsafe fn onEval(mut env: JNIEnv, _: JClass, id: jint, result: JString) {
match env.get_string(&result) {
Ok(result) => {
if let Some(cb) = EVAL_CALLBACKS
.get_or_init(Default::default)
.lock()
.unwrap()
.get(&id)
{
cb(result.into());
}
}
Err(e) => {
log::warn!("Failed to parse JString: {}", e);
}
}
}

pub unsafe fn ipc(mut env: JNIEnv, _: JClass, arg: JString) {
match env.get_string(&arg) {
Ok(arg) => {
#[cfg(feature = "tracing")]
let _span = tracing::info_span!("wry::ipc::handle").entered();

let arg = arg.to_string_lossy().to_string();
if let Some(ipc) = IPC.get() {
(ipc.handler)(arg)
Expand Down
9 changes: 9 additions & 0 deletions src/android/kotlin/RustWebView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ class RustWebView(context: Context, val initScripts: Array<String>): WebView(con
}
}

fun evalScript(id: Int, script: String) {
post {
super.evaluateJavascript(script) { result ->
onEval(id, result)
}
}
}

fun clearAllBrowsingData() {
try {
super.getContext().deleteDatabase("webviewCache.db")
Expand All @@ -90,6 +98,7 @@ class RustWebView(context: Context, val initScripts: Array<String>): WebView(con
}

private external fun shouldOverride(url: String): Boolean
private external fun onEval(id: Int, result: String)

{{class-extension}}
}
46 changes: 39 additions & 7 deletions src/android/main_pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use jni::{
JNIEnv,
};
use once_cell::sync::Lazy;
use std::os::unix::prelude::*;
use std::{os::unix::prelude::*, sync::atomic::Ordering};

use super::{find_class, PACKAGE};
use super::{find_class, EvalCallback, EVAL_CALLBACKS, EVAL_ID_GENERATOR, PACKAGE};

static CHANNEL: Lazy<(Sender<WebViewMessage>, Receiver<WebViewMessage>)> = Lazy::new(|| bounded(8));
pub static MAIN_PIPE: Lazy<[RawFd; 2]> = Lazy::new(|| {
Expand Down Expand Up @@ -194,14 +194,39 @@ impl<'a> MainPipe<'a> {

self.webview = Some(webview);
}
WebViewMessage::Eval(script) => {
WebViewMessage::Eval(script, callback) => {
if let Some(webview) = &self.webview {
let id = EVAL_ID_GENERATOR
.get_or_init(Default::default)
.fetch_add(1, Ordering::Relaxed);

#[cfg(feature = "tracing")]
let span = std::sync::Mutex::new(Some(SendEnteredSpan(
tracing::debug_span!("wry::eval").entered(),
)));

EVAL_CALLBACKS
.get_or_init(Default::default)
.lock()
.unwrap()
.insert(
id,
Box::new(move |result| {
#[cfg(feature = "tracing")]
span.lock().unwrap().take();

if let Some(callback) = &callback {
callback(result);
}
}),
);

let s = self.env.new_string(script)?;
self.env.call_method(
webview.as_obj(),
"evaluateJavascript",
"(Ljava/lang/String;Landroid/webkit/ValueCallback;)V",
&[(&s).into(), JObject::null().as_ref().into()],
"evalScript",
"(ILjava/lang/String;)V",
&[id.into(), (&s).into()],
)?;
}
}
Expand Down Expand Up @@ -340,7 +365,7 @@ fn set_background_color<'a>(

pub(crate) enum WebViewMessage {
CreateWebView(CreateWebViewAttributes),
Eval(String),
Eval(String, Option<EvalCallback>),
SetBackgroundColor(RGBA),
GetWebViewVersion(Sender<Result<String, Error>>),
GetUrl(Sender<String>),
Expand All @@ -362,3 +387,10 @@ pub(crate) struct CreateWebViewAttributes {
pub user_agent: Option<String>,
pub initialization_scripts: Vec<String>,
}

// SAFETY: only use this when you are sure the span will be dropped on the same thread it was entered
#[cfg(feature = "tracing")]
struct SendEnteredSpan(tracing::span::EnteredSpan);

#[cfg(feature = "tracing")]
unsafe impl Send for SendEnteredSpan {}
21 changes: 17 additions & 4 deletions src/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ use kuchiki::NodeRef;
use ndk::looper::{FdEvent, ForeignLooper};
use once_cell::sync::OnceCell;
use sha2::{Digest, Sha256};
use std::{borrow::Cow, sync::mpsc::channel};
use std::{
borrow::Cow,
collections::HashMap,
sync::{atomic::AtomicI32, mpsc::channel, Mutex},
};
use url::Url;

pub(crate) mod binding;
Expand Down Expand Up @@ -64,6 +68,12 @@ pub static ASSET_LOADER_DOMAIN: OnceCell<String> = OnceCell::new();

pub(crate) static PACKAGE: OnceCell<String> = OnceCell::new();

type EvalCallback = Box<dyn Fn(String) + Send + 'static>;

pub static EVAL_ID_GENERATOR: OnceCell<AtomicI32> = OnceCell::new();
pub static EVAL_CALLBACKS: once_cell::sync::OnceCell<Mutex<HashMap<i32, EvalCallback>>> =
once_cell::sync::OnceCell::new();

/// Sets up the necessary logic for wry to be able to create the webviews later.
pub unsafe fn android_setup(
package: &str,
Expand Down Expand Up @@ -299,8 +309,11 @@ impl InnerWebView {
Url::parse(uri.as_str()).unwrap()
}

pub fn eval(&self, js: &str, _callback: Option<impl Fn(String) + Send + 'static>) -> Result<()> {
MainPipe::send(WebViewMessage::Eval(js.into()));
pub fn eval(&self, js: &str, callback: Option<impl Fn(String) + Send + 'static>) -> Result<()> {
MainPipe::send(WebViewMessage::Eval(
js.into(),
callback.map(|c| Box::new(c) as Box<dyn Fn(String) + Send + 'static>),
));
Ok(())
}

Expand Down Expand Up @@ -344,7 +357,7 @@ impl InnerWebView {
}
}

pub fn set_bounds(&self, bounds: crate::Rect) {
pub fn set_bounds(&self, _bounds: crate::Rect) {
// Unsupported
}

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
//! libraries and prevent from building documentation on doc.rs fails.
//! - `linux-body`: Enables body support of custom protocol request on Linux. Requires
//! webkit2gtk v2.40 or above.
//! - `tracing`: enables [tracing] for `evaluate_script`, `ipc_handler` and `custom_protocols.
//!
//! [`tao`]: https://docs.rs/tao
//! [`winit`]: https://docs.rs/winit
Expand Down
39 changes: 26 additions & 13 deletions src/webkitgtk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ impl InnerWebView {

// Connect before registering as recommended by the docs
manager.connect_script_message_received(None, move |_m, msg| {
#[cfg(feature = "tracing")]
let _span = tracing::info_span!("wry::ipc::handle").entered();

if let Some(js) = msg.js_value() {
if let Some(ipc_handler) = &ipc_handler {
ipc_handler(js.to_string());
Expand Down Expand Up @@ -487,24 +490,27 @@ impl InnerWebView {
} else {
let cancellable: Option<&Cancellable> = None;

match callback {
Some(callback) => {
self.webview.run_javascript(js, cancellable, |result| {
let mut result_str = String::new();
#[cfg(feature = "tracing")]
let span = SendEnteredSpan(tracing::debug_span!("wry::eval").entered());

if let Ok(js_result) = result {
if let Some(js_value) = js_result.js_value() {
if let Some(json_str) = js_value.to_json(0) {
result_str = json_str.to_string();
}
self.webview.run_javascript(js, cancellable, |result| {
#[cfg(feature = "tracing")]
drop(span);

if let Some(callback) = callback {
let mut result_str = String::new();

if let Ok(js_result) = result {
if let Some(js_value) = js_result.js_value() {
if let Some(json_str) = js_value.to_json(0) {
result_str = json_str.to_string();
}
}
}

callback(result_str);
});
callback(result_str);
}
None => self.webview.run_javascript(js, cancellable, |_| ()),
};
});
}

Ok(())
Expand Down Expand Up @@ -700,3 +706,10 @@ pub fn platform_webview_version() -> Result<String> {
};
Ok(format!("{}.{}.{}", major, minor, patch))
}

// SAFETY: only use this when you are sure the span will be dropped on the same thread it was entered
#[cfg(feature = "tracing")]
struct SendEnteredSpan(tracing::span::EnteredSpan);

#[cfg(feature = "tracing")]
unsafe impl Send for SendEnteredSpan {}
9 changes: 9 additions & 0 deletions src/webkitgtk/web_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,16 @@ where
.register_uri_scheme_as_secure(name);

context.register_uri_scheme(name, move |request| {
#[cfg(feature = "tracing")]
let span =
tracing::info_span!("wry::custom_protocol::handle", uri = tracing::field::Empty).entered();

if let Some(uri) = request.uri() {
let uri = uri.as_str();

#[cfg(feature = "tracing")]
span.record("uri", uri);

// FIXME: Read the body (forms post)
#[allow(unused_mut)]
let mut http_request = Request::builder().uri(uri).method("GET");
Expand Down Expand Up @@ -393,6 +400,8 @@ where
request_.finish_with_response(&response);
});

#[cfg(feature = "tracing")]
let _span = tracing::info_span!("wry::custom_protocol::call_handler").entered();
handler(http_request, RequestAsyncResponder { responder });
} else {
request.finish_error(&mut glib::Error::new(
Expand Down
Loading
Loading