Skip to content

Commit

Permalink
Merge pull request #17 from simonbuchan/bind_async
Browse files Browse the repository at this point in the history
Add `bind_async()` and `bind_tokio()`
  • Loading branch information
Srinivasa314 authored Jun 7, 2021
2 parents e675f9b + 578a5bf commit 40a49b7
Show file tree
Hide file tree
Showing 7 changed files with 323 additions and 49 deletions.
36 changes: 30 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ license = "MIT OR Apache-2.0"
readme = "README.md"
repository = "https://github.com/Srinivasa314/alcro"

[[test]]
name = "integration_test"

[[test]]
name = "tokio_test"
required-features = ["tokio"]

[dependencies]
serde = { version = "1.0.125", features = ["derive"] }
serde_json = "1.0.64"
Expand All @@ -20,6 +27,8 @@ thiserror = "1.0.24"
tempfile = "3.2.0"
nix = "0.20.0"

tokio = { version = "1", features = ["rt"], optional = true }

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.9", features = ["namedpipeapi" , "synchapi" , "handleapi" ] }
os_str_bytes = "3.0.0"
Expand All @@ -32,3 +41,4 @@ mime_guess = "2.0.3"
futures = "0.3.13"
anyhow = "1.0.40"

tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
89 changes: 88 additions & 1 deletion src/chrome.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,60 @@ impl ToResultOfJSError for JSResult {
}
}

type BindingFunc = Arc<dyn Fn(&[JSObject]) -> JSResult + Sync + Send>;
/// Context for an async binding function.
pub struct BindingContext {
active: Option<ActiveBindingContext>,
}

impl BindingContext {
fn new(active: ActiveBindingContext) -> Self {
Self {
active: Some(active),
}
}

/// The arguments from JS.
pub fn args(&self) -> &[JSObject] {
match &self.active {
None => &[],
Some(active) => active.payload["args"].as_array().expect("Expected array"),
}
}

/// Completes the JS function successfully. Equivalent to `complete(Ok(result))`
pub fn done(self, result: JSObject) {
self.complete(Ok(result))
}

/// Completes the JS function with an error. Equivalent to `complete(Err(error))`
pub fn err(self, error: JSObject) {
self.complete(Err(error))
}

/// Completes the JS function, either successfully or not. Takes the [`BindingContext`] by
/// value as it releases the outstanding call on the Chrome(ium) side.
pub fn complete(mut self, result: JSResult) {
if let Some(incomplete) = self.active.take() {
complete_binding(incomplete, result)
}
}
}

impl Drop for BindingContext {
fn drop(&mut self) {
if let Some(incomplete) = self.active.take() {
complete_binding(incomplete, Ok(JSObject::Null))
}
}
}

struct ActiveBindingContext {
chrome: Arc<Chrome>,
payload: JSObject,
context_id: i64,
}

type BindingFunc = Arc<dyn Fn(BindingContext) + Sync + Send>;

pub struct Chrome {
id: AtomicI32,
Expand Down Expand Up @@ -391,6 +444,40 @@ pub fn bind(c: Arc<Chrome>, name: &str, f: BindingFunc) -> Result<(), JSError> {
eval(Arc::clone(&c), &script).to_result_of_jserror()
}

fn complete_binding(context: ActiveBindingContext, result: JSResult) {
let (r, e) = match result {
Ok(x) => (x.to_string(), r#""""#.to_string()),
Err(e) => ("".to_string(), e.to_string()),
};

let expr = format!(
r"
if ({error}) {{
window['{name}']['errors'].get({seq})({error});
}} else {{
window['{name}']['callbacks'].get({seq})({result});
}}
window['{name}']['callbacks'].delete({seq});
window['{name}']['errors'].delete({seq});
",
name = context.payload["name"].as_str().expect("Expected string"),
seq = context.payload["seq"].as_i64().expect("Expected i64"),
result = r,
error = e
);

if let Err(e) = send(
context.chrome,
"Runtime.evaluate",
&json!({
"expression":expr,
"contextId":context.context_id
}),
) {
eprintln!("{}", e);
}
}

pub fn close(c: Arc<Chrome>) {
std::thread::spawn(move || {
if let Err(e) = send(c, "Browser.close", &json!({})) {
Expand Down
47 changes: 6 additions & 41 deletions src/chrome/devtools.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{Chrome, JSObject, JSResult};
use super::{ActiveBindingContext, BindingContext, Chrome, JSObject, JSResult};
use super::{PipeReader, PipeWriter};
use crossbeam_channel::{bounded, Sender};
use serde_json::json;
Expand Down Expand Up @@ -133,45 +133,10 @@ fn binding_called(c: Arc<Chrome>, name: &str, payload: JSObject, context_id: i64
None => None,
};
if let Some(binding) = binding {
let c = Arc::clone(&c);
std::thread::spawn(move || {
let result: Result<String, String> =
match binding(payload["args"].as_array().expect("Expected array")) {
Err(e) => Err(e.to_string()),
Ok(v) => Ok(v.to_string()),
};

let (r, e) = match result {
Ok(x) => (x, r#""""#.to_string()),
Err(e) => ("".to_string(), e),
};

let expr = format!(
r"
if ({error}) {{
window['{name}']['errors'].get({seq})({error});
}} else {{
window['{name}']['callbacks'].get({seq})({result});
}}
window['{name}']['callbacks'].delete({seq});
window['{name}']['errors'].delete({seq});
",
name = payload["name"].as_str().expect("Expected string"),
seq = payload["seq"].as_i64().expect("Expected i64"),
result = r,
error = e
);

if let Err(e) = send(
Arc::clone(&c),
"Runtime.evaluate",
&json!({
"expression":expr,
"contextId":context_id
}),
) {
eprintln!("{}", e);
}
});
binding(BindingContext::new(ActiveBindingContext {
chrome: c,
payload,
context_id,
}))
}
}
Loading

0 comments on commit 40a49b7

Please sign in to comment.