firefox_webdriver/browser/tab/
navigation.rs

1//! Tab navigation methods.
2
3use tracing::debug;
4
5use crate::error::Result;
6use crate::protocol::{BrowsingContextCommand, Command};
7
8use super::Tab;
9
10// ============================================================================
11// Tab - Navigation
12// ============================================================================
13
14impl Tab {
15    /// Navigates to a URL.
16    ///
17    /// # Arguments
18    ///
19    /// * `url` - The URL to navigate to
20    ///
21    /// # Errors
22    ///
23    /// Returns an error if navigation fails.
24    pub async fn goto(&self, url: &str) -> Result<()> {
25        debug!(url = %url, tab_id = %self.inner.tab_id, "Navigating");
26
27        let command = Command::BrowsingContext(BrowsingContextCommand::Navigate {
28            url: url.to_string(),
29        });
30
31        self.send_command(command).await?;
32        Ok(())
33    }
34
35    /// Loads HTML content directly into the page.
36    ///
37    /// Useful for testing with inline HTML without needing a server.
38    ///
39    /// # Arguments
40    ///
41    /// * `html` - HTML content to load
42    ///
43    /// # Example
44    ///
45    /// ```ignore
46    /// tab.load_html("<html><body><h1>Test</h1></body></html>").await?;
47    /// ```
48    pub async fn load_html(&self, html: &str) -> Result<()> {
49        debug!(tab_id = %self.inner.tab_id, html_len = html.len(), "Loading HTML content");
50
51        let escaped_html = html
52            .replace('\\', "\\\\")
53            .replace('`', "\\`")
54            .replace("${", "\\${");
55
56        let script = format!(
57            r#"(function() {{
58                const html = `{}`;
59                const parser = new DOMParser();
60                const doc = parser.parseFromString(html, 'text/html');
61                const newTitle = doc.querySelector('title');
62                if (newTitle) {{ document.title = newTitle.textContent; }}
63                const newBody = doc.body;
64                if (newBody) {{
65                    document.body.innerHTML = newBody.innerHTML;
66                    for (const attr of newBody.attributes) {{
67                        document.body.setAttribute(attr.name, attr.value);
68                    }}
69                }}
70                const newHead = doc.head;
71                if (newHead) {{
72                    for (const child of newHead.children) {{
73                        if (child.tagName !== 'TITLE') {{
74                            document.head.appendChild(child.cloneNode(true));
75                        }}
76                    }}
77                }}
78            }})();"#,
79            escaped_html
80        );
81
82        self.execute_script(&script).await?;
83        Ok(())
84    }
85
86    /// Reloads the current page.
87    pub async fn reload(&self) -> Result<()> {
88        debug!(tab_id = %self.inner.tab_id, "Reloading page");
89        let command = Command::BrowsingContext(BrowsingContextCommand::Reload);
90        self.send_command(command).await?;
91        Ok(())
92    }
93
94    /// Navigates back in history.
95    pub async fn back(&self) -> Result<()> {
96        debug!(tab_id = %self.inner.tab_id, "Navigating back");
97        let command = Command::BrowsingContext(BrowsingContextCommand::GoBack);
98        self.send_command(command).await?;
99        Ok(())
100    }
101
102    /// Navigates forward in history.
103    pub async fn forward(&self) -> Result<()> {
104        debug!(tab_id = %self.inner.tab_id, "Navigating forward");
105        let command = Command::BrowsingContext(BrowsingContextCommand::GoForward);
106        self.send_command(command).await?;
107        Ok(())
108    }
109
110    /// Gets the current page title.
111    pub async fn get_title(&self) -> Result<String> {
112        debug!(tab_id = %self.inner.tab_id, "Getting page title");
113        let command = Command::BrowsingContext(BrowsingContextCommand::GetTitle);
114        let response = self.send_command(command).await?;
115
116        let title = response
117            .result
118            .as_ref()
119            .and_then(|v| v.get("title"))
120            .and_then(|v| v.as_str())
121            .unwrap_or("")
122            .to_string();
123
124        debug!(tab_id = %self.inner.tab_id, title = %title, "Got page title");
125        Ok(title)
126    }
127
128    /// Gets the current URL.
129    pub async fn get_url(&self) -> Result<String> {
130        debug!(tab_id = %self.inner.tab_id, "Getting page URL");
131        let command = Command::BrowsingContext(BrowsingContextCommand::GetUrl);
132        let response = self.send_command(command).await?;
133
134        let url = response
135            .result
136            .as_ref()
137            .and_then(|v| v.get("url"))
138            .and_then(|v| v.as_str())
139            .unwrap_or("")
140            .to_string();
141
142        debug!(tab_id = %self.inner.tab_id, url = %url, "Got page URL");
143        Ok(url)
144    }
145
146    /// Focuses this tab (makes it active).
147    pub async fn focus(&self) -> Result<()> {
148        debug!(tab_id = %self.inner.tab_id, "Focusing tab");
149        let command = Command::BrowsingContext(BrowsingContextCommand::FocusTab);
150        self.send_command(command).await?;
151        Ok(())
152    }
153
154    /// Focuses the window containing this tab.
155    pub async fn focus_window(&self) -> Result<()> {
156        debug!(tab_id = %self.inner.tab_id, "Focusing window");
157        let command = Command::BrowsingContext(BrowsingContextCommand::FocusWindow);
158        self.send_command(command).await?;
159        Ok(())
160    }
161
162    /// Closes this tab.
163    pub async fn close(&self) -> Result<()> {
164        debug!(tab_id = %self.inner.tab_id, "Closing tab");
165        let command = Command::BrowsingContext(BrowsingContextCommand::CloseTab);
166        self.send_command(command).await?;
167        Ok(())
168    }
169
170    /// Gets the page source HTML.
171    pub async fn get_page_source(&self) -> Result<String> {
172        debug!(tab_id = %self.inner.tab_id, "Getting page source");
173        let result = self
174            .execute_script("return document.documentElement.outerHTML")
175            .await?;
176        Ok(result.as_str().unwrap_or("").to_string())
177    }
178}