use crate::domains::{DispatchContext, DomainResult};
use crate::protocol::CdpError;
use oxibrowser_core::js::{js_dispatch_key_event, js_dispatch_mouse_event, js_insert_text};
use serde_json::{json, Value};
use std::time::{SystemTime, UNIX_EPOCH};
pub async fn handle(method: &str, params: Option<Value>, ctx: &DispatchContext) -> DomainResult {
match method {
"dispatchKeyEvent" => dispatch_key_event(params, ctx).await,
"dispatchMouseEvent" => dispatch_mouse_event(params, ctx).await,
"dispatchDragEvent" => dispatch_drag_event(params, ctx).await,
"insertText" => insert_text(params, ctx).await,
"imeSetComposition" => ime_set_composition(params, ctx).await,
"synthesizePinchGesture" => Ok(Some(json!({}))),
"synthesizeScrollGesture" => Ok(Some(json!({}))),
_ => Err(CdpError {
code: -32601,
message: format!("Input.{} not implemented", method),
}),
}
}
async fn dispatch_key_event(params: Option<Value>, ctx: &DispatchContext) -> DomainResult {
let p = params.ok_or_else(|| CdpError {
code: -32602,
message: "dispatchKeyEvent requires parameters".to_string(),
})?;
let event_type = p.get("type").and_then(|v| v.as_str()).unwrap_or("keyDown");
let key = p.get("key").and_then(|v| v.as_str()).unwrap_or("");
let code = p.get("code").and_then(|v| v.as_str()).unwrap_or("");
let modifiers = calculate_modifiers(&p);
let timestamp = p
.get("timestamp")
.and_then(|v| v.as_f64())
.unwrap_or_else(current_timestamp_ms);
tracing::debug!(
"Input.dispatchKeyEvent: type={}, key={}, code={}, modifiers={}",
event_type,
key,
code,
modifiers
);
let js = js_dispatch_key_event(key, code, event_type, modifiers, timestamp);
let mut session_guard = ctx.session.write().await;
let result = session_guard.evaluate_js(&js).await;
match result {
Ok(val) => {
tracing::debug!("dispatchKeyEvent result: {:?}", val);
ctx.events.send_page_event(
"Input.dispatchKeyEvent",
json!({
"type": event_type,
"key": key,
"code": code,
"modifiers": modifiers,
}),
);
}
Err(e) => {
tracing::warn!("dispatchKeyEvent JS eval failed: {}", e);
}
}
Ok(Some(json!({
"timestamp": timestamp,
})))
}
async fn insert_text(params: Option<Value>, ctx: &DispatchContext) -> DomainResult {
let p = params.ok_or_else(|| CdpError {
code: -32602,
message: "insertText requires parameters".to_string(),
})?;
let text = p.get("text").and_then(|v| v.as_str()).unwrap_or("");
tracing::debug!("Input.insertText: text={}", text);
let js = js_insert_text(text);
let mut session_guard = ctx.session.write().await;
let _ = session_guard.evaluate_js(&js).await;
Ok(Some(json!({})))
}
async fn ime_set_composition(params: Option<Value>, _ctx: &DispatchContext) -> DomainResult {
let p = params.ok_or_else(|| CdpError {
code: -32602,
message: "imeSetComposition requires parameters".to_string(),
})?;
let selections = p
.get("segments")
.and_then(|v| v.as_array())
.map(|a| a.len())
.unwrap_or(0);
tracing::debug!("Input.imeSetComposition: {} segments", selections);
Ok(Some(json!({})))
}
async fn dispatch_mouse_event(params: Option<Value>, ctx: &DispatchContext) -> DomainResult {
let p = params.ok_or_else(|| CdpError {
code: -32602,
message: "dispatchMouseEvent requires parameters".to_string(),
})?;
let event_type = p.get("type").and_then(|v| v.as_str()).unwrap_or("mouseMoved");
let x = p.get("x").and_then(|v| v.as_f64()).unwrap_or(0.0);
let y = p.get("y").and_then(|v| v.as_f64()).unwrap_or(0.0);
let button = p.get("button").and_then(|v| v.as_str()).unwrap_or("none");
let click_count = p.get("clickCount").and_then(|v| v.as_i64()).unwrap_or(0) as u32;
tracing::debug!(
"Input.dispatchMouseEvent: type={}, x={}, y={}, button={}, clicks={}",
event_type,
x,
y,
button,
click_count
);
let js = js_dispatch_mouse_event(x, y, event_type, button, click_count);
let mut session_guard = ctx.session.write().await;
let result = session_guard.evaluate_js(&js).await;
match result {
Ok(val) => {
tracing::debug!("dispatchMouseEvent result: {:?}", val);
ctx.events.send_page_event(
"Input.dispatchMouseEvent",
json!({
"type": event_type,
"x": x,
"y": y,
"button": button,
"clickCount": click_count,
}),
);
}
Err(e) => {
tracing::warn!("dispatchMouseEvent JS eval failed: {}", e);
}
}
Ok(Some(json!({})))
}
async fn dispatch_drag_event(params: Option<Value>, _ctx: &DispatchContext) -> DomainResult {
let p = params.ok_or_else(|| CdpError {
code: -32602,
message: "dispatchDragEvent requires parameters".to_string(),
})?;
let event_type = p.get("type").and_then(|v| v.as_str()).unwrap_or("dragOver");
let x = p.get("x").and_then(|v| v.as_f64()).unwrap_or(0.0);
let y = p.get("y").and_then(|v| v.as_f64()).unwrap_or(0.0);
tracing::debug!(
"Input.dispatchDragEvent: type={}, x={}, y={}",
event_type,
x,
y
);
Ok(Some(json!({})))
}
fn calculate_modifiers(params: &serde_json::Value) -> u32 {
let mut m = 0u32;
if params.get("modifiers").and_then(|v| v.as_i64()).unwrap_or(0) > 0 {
m |= 1;
}
if params.get("shiftKey").and_then(|v| v.as_bool()).unwrap_or(false) {
m |= 8;
}
if params.get("ctrlKey").and_then(|v| v.as_bool()).unwrap_or(false) {
m |= 4;
}
if params.get("altKey").and_then(|v| v.as_bool()).unwrap_or(false) {
m |= 2;
}
if params.get("metaKey").and_then(|v| v.as_bool()).unwrap_or(false) {
m |= 16;
}
m
}
fn current_timestamp_ms() -> f64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs_f64()
* 1000.0
}