wdc/
geckodrv.rs

1// Copyright (C) 2023  Michael Lee
2//
3// This file is part of Wdc.
4//
5// Wdc is free software: you can redistribute it and/or modify it under the
6// terms of the GNU General Public License as published by the Free Software
7// Foundation, either version 3 of the License, or (at your option) any later
8// version.
9//
10// Wdc is distributed in the hope that it will be useful, but WITHOUT ANY
11// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12// A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License along with
15// Wdc. If not, see <https://www.gnu.org/licenses/>.
16
17use std::sync::Arc;
18
19use crate::wdcmd::session::{FirefoxCapa, GeckoCapRequ, GeckoSessResult};
20use crate::wdcmd::session::{FirefoxCapaGetter, GeckoCapRequSetter};
21use crate::wdcmd::session::{W3cCapaGetter, W3cSessResultGetter};
22
23use crate::CreateW3cSession;
24use crate::CreateWebDrvClient;
25use crate::WdcError;
26
27use crate::genericdrv::WebDrvClient;
28
29use crate::httpp::HttpRequestParts;
30use crate::httpp::HttpResponseParts;
31
32use crate::genericdrv::check_fail_drvcmd;
33
34// GeckoDriver //
35
36/// A placeholder for Mozilla GeckoDriver.
37#[cfg(feature = "firefox")]
38#[cfg_attr(doc_cfg, doc(cfg(feature = "firefox")))]
39#[derive(Debug, Default)]
40pub struct GeckoDriver;
41
42///
43/// Create a GeckoDriver-specific WebDriver client.
44impl CreateWebDrvClient for GeckoDriver {
45    fn new(rhost: &str, rport: u16) -> WebDrvClient<Self> {
46        WebDrvClient {
47            kind: GeckoDriver,
48            rhost: rhost.to_string(),
49            rport,
50            rstream: None,
51            ssids: vec![],
52        }
53    }
54}
55
56impl<'de, 'c1, 'c2> CreateW3cSession<'de, 'c1, 'c2> for GeckoDriver {
57    type CapRequ<'r> = GeckoCapRequ<'r>where 'c1: 'r, 'c2: 'r;
58    type Capa<'a> = FirefoxCapa<'a>;
59    type SessResult = GeckoSessResult<'de>;
60}
61
62///
63/// Initialize a WebDriver client instance.
64///
65/// It differs [`init_singl`] in the underlying assumption of session's backing
66/// web browser, here assumes [Firefox](https://www.mozilla.org/en-US/firefox/).
67#[cfg(feature = "firefox")]
68#[cfg_attr(doc_cfg, doc(cfg(feature = "firefox")))]
69pub fn init_singl_ff(
70    rhost: &str,
71    rport: u16,
72    capa: &(impl W3cCapaGetter + FirefoxCapaGetter),
73    ready_timeout: u32,
74) -> Result<WebDrvClient<GeckoDriver>, WdcError> {
75    let mut wdc = GeckoDriver::new(rhost, rport);
76
77    wdc.ensure_remote_connected()?;
78
79    let ready_timeout_in_micros = (ready_timeout * 1000000) as u64;
80    let mut already_wait = 0u64;
81    let wait_each_round = 100u64;
82    let mut ready_or_not = false;
83
84    while already_wait < ready_timeout_in_micros {
85        match wdc.is_ready() {
86            Ok(_) => {
87                break;
88            }
89            Err(WdcError::DriverNotReadyBusySession) => {
90                std::thread::sleep(std::time::Duration::from_micros(wait_each_round));
91                already_wait += wait_each_round;
92                continue;
93            }
94            Err(_e) => {
95                dbgg!(_e);
96                break;
97            }
98        }
99    }
100
101    while already_wait < ready_timeout_in_micros {
102        match wdc.ff_session_singl(capa) {
103            Ok(_) => {
104                ready_or_not = true;
105                break;
106            }
107            Err(WdcError::BusyCreateSession) => {
108                std::thread::sleep(std::time::Duration::from_micros(wait_each_round));
109                already_wait += wait_each_round;
110                continue;
111            }
112            Err(_e) => {
113                dbgg!(_e);
114                break;
115            }
116        }
117    }
118
119    dbgg!(already_wait);
120
121    if ready_or_not {
122        Ok(wdc)
123    } else {
124        Err(WdcError::WebDriverNotReady)
125    }
126}
127
128impl WebDrvClient<GeckoDriver> {
129    fn ff_session_singl(
130        &mut self,
131        capa: &(impl W3cCapaGetter + FirefoxCapaGetter),
132    ) -> Result<(), WdcError> {
133        if self.rstream.is_none() {
134            return Err(WdcError::WebDriverRemoteConnectionFailed);
135        };
136
137        let rs = Arc::clone(self.rstream.as_ref().unwrap());
138        let mut stream = rs.lock().unwrap();
139
140        let anycapa = FirefoxCapa::default(); // before requ
141        let mut requ = GeckoCapRequ::default();
142
143        requ.allow(&anycapa); // tolerant match
144
145        requ.mandate(capa);
146
147        let mut req = HttpRequestParts::from_scratch();
148
149        let mut mb = Vec::<u8>::new();
150        mb.extend(r#"{"capabilities":"#.as_bytes());
151        mb.extend(serde_json::to_vec(&requ).expect("ser"));
152        mb.extend(r#"}"#.as_bytes());
153
154        dbgg!(String::from_utf8_lossy(&mb));
155
156        req.http1p1()
157            .post("/session")
158            .host(&self.raddr())
159            .msgbody_from_slice(&mb)
160            .content_type("application/json")
161            .send_through(&mut stream)
162            .unwrap();
163
164        let resp = HttpResponseParts::from_stream(&mut stream, None, 0, 0).unwrap();
165
166        dbgg!(String::from_utf8_lossy(resp.msgbody()));
167
168        if resp.is_ok() {
169            let deser_result;
170
171            run_diag!("deser_resp", {
172                deser_result = serde_json::from_slice::<GeckoSessResult>(resp.msgbody())
173            });
174
175            match deser_result {
176                Ok(sess) => {
177                    self.add_ssid(sess.session_id().to_string());
178                    Ok(())
179                }
180                _ => Err(WdcError::Buggy),
181            }
182        } else {
183            check_fail_drvcmd(resp.msgbody())?;
184            Err(WdcError::Buggy) // unreachable
185        }
186    }
187}