use crate::browser::{Browser, Element, StaticElement, Tab};
use crate::launcher::BrowserOptions;
use crate::session::{PostData, SessionOptions, SessionPage};
use crate::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PageMode {
Driver,
Session,
}
pub struct WebPage {
mode: PageMode,
browser: Option<Browser>,
tab: Option<Tab>,
session: SessionPage,
browser_opts: BrowserOptions,
}
impl WebPage {
pub fn new_session() -> Result<Self> {
Self::new_session_with(SessionOptions::new(), BrowserOptions::new().headless(true))
}
pub fn new_session_with(session: SessionOptions, browser_opts: BrowserOptions) -> Result<Self> {
Ok(Self {
mode: PageMode::Session,
browser: None,
tab: None,
session: SessionPage::new(session)?,
browser_opts,
})
}
pub async fn new_driver(browser_opts: BrowserOptions) -> Result<Self> {
Self::new_driver_with(browser_opts, SessionOptions::new()).await
}
pub async fn new_driver_with(
browser_opts: BrowserOptions,
session: SessionOptions,
) -> Result<Self> {
let browser = Browser::launch(browser_opts.clone()).await?;
let tab = browser.latest_tab().await?;
Ok(Self {
mode: PageMode::Driver,
browser: Some(browser),
tab: Some(tab),
session: SessionPage::new(session)?,
browser_opts,
})
}
pub fn mode(&self) -> PageMode {
self.mode
}
pub fn is_driver(&self) -> bool {
self.mode == PageMode::Driver
}
pub async fn change_mode(&mut self, mode: PageMode) -> Result<()> {
if mode == self.mode {
return Ok(());
}
match mode {
PageMode::Session => {
if let Some(tab) = &self.tab {
self.session.load_cookies_from_tab(tab).await?;
}
self.mode = PageMode::Session;
}
PageMode::Driver => {
let tab = self.ensure_driver().await?;
self.session.apply_cookies_to_tab(&tab).await?;
self.mode = PageMode::Driver;
}
}
Ok(())
}
pub async fn get(&mut self, url: &str) -> Result<bool> {
match self.mode {
PageMode::Session => self.session.get(url).await,
PageMode::Driver => self.ensure_driver().await?.get(url).await,
}
}
pub async fn post(&mut self, url: &str, data: PostData) -> Result<bool> {
self.session.post(url, data).await
}
pub async fn html(&self) -> Result<String> {
match self.mode {
PageMode::Session => Ok(self.session.html().to_string()),
PageMode::Driver => self.tab.as_ref().ok_or_else(no_tab)?.html().await,
}
}
pub async fn title(&self) -> Result<String> {
match self.mode {
PageMode::Session => self.session.title(),
PageMode::Driver => self.tab.as_ref().ok_or_else(no_tab)?.title().await,
}
}
pub async fn url(&self) -> Result<String> {
match self.mode {
PageMode::Session => Ok(self.session.url().to_string()),
PageMode::Driver => self.tab.as_ref().ok_or_else(no_tab)?.url().await,
}
}
pub async fn s_ele(&self, selector: &str) -> Result<StaticElement> {
match self.mode {
PageMode::Session => self.session.s_ele(selector),
PageMode::Driver => self.tab.as_ref().ok_or_else(no_tab)?.s_ele(selector).await,
}
}
pub async fn s_eles(&self, selector: &str) -> Result<Vec<StaticElement>> {
match self.mode {
PageMode::Session => self.session.s_eles(selector),
PageMode::Driver => self.tab.as_ref().ok_or_else(no_tab)?.s_eles(selector).await,
}
}
pub async fn ele(&self, selector: &str) -> Result<Element> {
match self.mode {
PageMode::Driver => self.tab.as_ref().ok_or_else(no_tab)?.ele(selector).await,
PageMode::Session => Err(session_no_live("ele")),
}
}
pub async fn eles(&self, selector: &str) -> Result<Vec<Element>> {
match self.mode {
PageMode::Driver => self.tab.as_ref().ok_or_else(no_tab)?.eles(selector).await,
PageMode::Session => Err(session_no_live("eles")),
}
}
pub fn tab(&self) -> Option<&Tab> {
self.tab.as_ref()
}
pub fn session(&self) -> &SessionPage {
&self.session
}
pub fn session_mut(&mut self) -> &mut SessionPage {
&mut self.session
}
pub async fn quit(self) -> Result<()> {
if let Some(b) = self.browser {
b.quit().await?;
}
Ok(())
}
async fn ensure_driver(&mut self) -> Result<Tab> {
if self.tab.is_none() {
let browser = Browser::launch(self.browser_opts.clone()).await?;
let tab = browser.latest_tab().await?;
self.browser = Some(browser);
self.tab = Some(tab);
}
self.tab
.clone()
.ok_or_else(|| Error::Other("浏览器启动后仍无标签".into()))
}
}
fn no_tab() -> Error {
Error::Other("Driver 模式尚未启动浏览器".into())
}
fn session_no_live(name: &str) -> Error {
Error::Other(format!(
"{name}() 仅 Driver 模式可用;Session 模式请用 s_{name}()(静态解析)"
))
}