use serde::{Deserialize, Serialize};
use crate::bidi::BiDi;
use crate::bidi::command::{BidiCommand, BidiEvent, Empty};
use crate::bidi::error::BidiError;
use crate::bidi::ids::{BrowsingContextId, ChannelId, PreloadScriptId, RealmId};
use crate::common::protocol::string_enum;
string_enum! {
pub enum ResultOwnership {
Root = "root",
None = "none",
}
}
#[derive(Debug, Clone, Serialize)]
#[serde(untagged)]
pub enum Target {
Realm {
realm: RealmId,
},
Context {
context: BrowsingContextId,
#[serde(skip_serializing_if = "Option::is_none")]
sandbox: Option<String>,
},
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Evaluate {
pub expression: String,
pub target: Target,
pub await_promise: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub result_ownership: Option<ResultOwnership>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_activation: Option<bool>,
}
impl BidiCommand for Evaluate {
const METHOD: &'static str = "script.evaluate";
type Returns = EvaluateResult;
}
#[derive(Debug, Clone, Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum EvaluateResult {
Success {
realm: RealmId,
result: serde_json::Value,
},
Exception {
realm: RealmId,
#[serde(rename = "exceptionDetails")]
exception_details: serde_json::Value,
},
}
impl EvaluateResult {
pub fn ok_value(&self) -> Option<&serde_json::Value> {
match self {
EvaluateResult::Success {
result,
..
} => Some(result),
_ => None,
}
}
pub fn is_exception(&self) -> bool {
matches!(self, EvaluateResult::Exception { .. })
}
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CallFunction {
pub function_declaration: String,
pub await_promise: bool,
pub target: Target,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub arguments: Vec<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub this: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub result_ownership: Option<ResultOwnership>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_activation: Option<bool>,
}
impl BidiCommand for CallFunction {
const METHOD: &'static str = "script.callFunction";
type Returns = EvaluateResult;
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AddPreloadScript {
pub function_declaration: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub arguments: Vec<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sandbox: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub contexts: Vec<BrowsingContextId>,
}
impl BidiCommand for AddPreloadScript {
const METHOD: &'static str = "script.addPreloadScript";
type Returns = AddPreloadScriptResult;
}
#[derive(Debug, Clone, Deserialize)]
pub struct AddPreloadScriptResult {
pub script: PreloadScriptId,
}
#[derive(Debug, Clone, Serialize)]
pub struct RemovePreloadScript {
pub script: PreloadScriptId,
}
impl BidiCommand for RemovePreloadScript {
const METHOD: &'static str = "script.removePreloadScript";
type Returns = Empty;
}
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetRealms {
#[serde(skip_serializing_if = "Option::is_none")]
pub context: Option<BrowsingContextId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#type: Option<String>,
}
impl BidiCommand for GetRealms {
const METHOD: &'static str = "script.getRealms";
type Returns = GetRealmsResult;
}
#[derive(Debug, Clone, Deserialize)]
pub struct GetRealmsResult {
pub realms: Vec<RealmInfo>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RealmInfo {
pub realm: RealmId,
pub origin: String,
#[serde(rename = "type")]
pub realm_type: String,
#[serde(default)]
pub context: Option<BrowsingContextId>,
#[serde(flatten)]
pub extra: serde_json::Map<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize)]
pub struct Disown {
pub handles: Vec<String>,
pub target: Target,
}
impl BidiCommand for Disown {
const METHOD: &'static str = "script.disown";
type Returns = Empty;
}
pub(crate) mod events {
use super::*;
#[derive(Debug, Clone, Deserialize)]
pub struct RealmCreated(pub RealmInfo);
impl BidiEvent for RealmCreated {
const METHOD: &'static str = "script.realmCreated";
}
#[derive(Debug, Clone, Deserialize)]
pub struct RealmDestroyed {
pub realm: RealmId,
}
impl BidiEvent for RealmDestroyed {
const METHOD: &'static str = "script.realmDestroyed";
}
#[derive(Debug, Clone, Deserialize)]
pub struct Message {
pub channel: ChannelId,
pub data: serde_json::Value,
pub source: serde_json::Value,
}
impl BidiEvent for Message {
const METHOD: &'static str = "script.message";
}
}
#[derive(Debug)]
pub struct ScriptModule<'a> {
bidi: &'a BiDi,
}
impl<'a> ScriptModule<'a> {
pub(crate) fn new(bidi: &'a BiDi) -> Self {
Self {
bidi,
}
}
pub async fn evaluate(
&self,
context: BrowsingContextId,
expression: impl Into<String>,
await_promise: bool,
) -> Result<EvaluateResult, BidiError> {
self.bidi
.send(Evaluate {
expression: expression.into(),
target: Target::Context {
context,
sandbox: None,
},
await_promise,
result_ownership: None,
user_activation: None,
})
.await
}
pub async fn call_function(
&self,
context: BrowsingContextId,
function_declaration: impl Into<String>,
await_promise: bool,
) -> Result<EvaluateResult, BidiError> {
self.bidi
.send(CallFunction {
function_declaration: function_declaration.into(),
await_promise,
target: Target::Context {
context,
sandbox: None,
},
arguments: vec![],
this: None,
result_ownership: None,
user_activation: None,
})
.await
}
pub async fn add_preload_script(
&self,
function_declaration: impl Into<String>,
) -> Result<AddPreloadScriptResult, BidiError> {
self.bidi
.send(AddPreloadScript {
function_declaration: function_declaration.into(),
arguments: vec![],
sandbox: None,
contexts: vec![],
})
.await
}
pub async fn remove_preload_script(&self, script: PreloadScriptId) -> Result<(), BidiError> {
self.bidi
.send(RemovePreloadScript {
script,
})
.await?;
Ok(())
}
pub async fn get_realms(&self) -> Result<GetRealmsResult, BidiError> {
self.bidi
.send(GetRealms {
context: None,
r#type: None,
})
.await
}
}