Skip to content

Instantly share code, notes, and snippets.

@estin
Last active November 28, 2021 08:53
Show Gist options
  • Save estin/fca8782e5805403423207b7b67eba8f6 to your computer and use it in GitHub Desktop.
Save estin/fca8782e5805403423207b7b67eba8f6 to your computer and use it in GitHub Desktop.
// ### Usage
// $ cargo run
// open develop consonle on opened browser
// and execute in consle myPostMessage("it works").then(r => console.log("Result is:", r));
// ### Cargo.toml
//
// [package]
// name = "reddit-ex"
// version = "0.1.0"
// edition = "2018"
// [dependencies]
// glib = "0.14"
// gtk = "0.14"
// gio = "0.14"
// webkit2gtk = { version = "0.14", features = ["v2_26"] }
// serde = { version = "1.0", features = ["derive"] }
// serde_json = "1.0"
// src/main.rs
use gio::Cancellable;
use gtk::{
traits::{ContainerExt, WidgetExt},
Inhibit, Window, WindowType,
};
use serde::{Deserialize, Serialize};
use webkit2gtk::traits::{SettingsExt, UserContentManagerExt, WebViewExt};
use webkit2gtk::{self, LoadEvent, UserContentManager, WebContext, WebView, WebViewExtManual};
#[derive(Deserialize)]
struct Request {
id: usize,
data: serde_json::Value,
}
#[derive(Serialize)]
struct Response {
id: usize,
data: serde_json::Value,
}
fn main() {
gtk::init().unwrap();
let window = Window::new(WindowType::Toplevel);
let context = WebContext::default().unwrap();
let ucm = UserContentManager::new();
let webview = WebView::new_with_context_and_user_content_manager(&context, &ucm);
webview.load_uri("https://crates.io/");
window.add(&webview);
let settings = WebViewExt::settings(&webview).unwrap();
settings.set_enable_developer_extras(true);
// On page load inject javascript to handle request-response communication
webview.connect_load_changed(|wv, event| {
if event != LoadEvent::Finished {
return;
}
let cancellable: Option<&Cancellable> = None;
wv.run_javascript(
"
window._requests = {};
window._request_id = 0;
window.myPostMessage = function (data) {
return new Promise((resolve, reject) => {
window._request_id += 1;
window._requests[window._request_id] = [resolve, reject];
let request = JSON.stringify({id: window._request_id, data: data});
console.log('Request', request);
window.webkit.messageHandlers.external.postMessage(request);
});
};
window._myPostMessageResponse = function(response) {
console.log('Response', response);
let [resolve, reject] = window._requests[response.id];
resolve(response.data);
delete window._requests[response.id];
};
",
cancellable,
|result| {
println!("inject internal js code result {:?}", result);
},
);
});
window.show_all();
webview
.user_content_manager()
.unwrap()
.register_script_message_handler("external");
webview
.user_content_manager()
.unwrap()
.connect_script_message_received(Some("external"), move |_ucm, jsr| {
let ctx = jsr.global_context().unwrap();
let val = jsr.value().unwrap();
let request = val.to_string(&ctx).unwrap();
println!("Request {}", request);
let request: Request = serde_json::from_str(&request).unwrap();
// Some app logic
// TODO
// genereate response
let response = serde_json::to_string(&Response {
id: request.id,
data: serde_json::Value::String(format!("pong: {}", request.data)),
})
.unwrap();
println!("Response {}", response);
let cancellable: Option<&Cancellable> = None;
webview.run_javascript(
&format!("window._myPostMessageResponse({});", response),
cancellable,
|result| {
println!("myPostMessageResponse result {:?}", result);
},
);
});
window.connect_delete_event(|_, _| {
gtk::main_quit();
Inhibit(false)
});
gtk::main();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment