firefox_webdriver/browser/tab/
navigation.rs1use tracing::debug;
4
5use crate::error::Result;
6use crate::protocol::{BrowsingContextCommand, Command};
7
8use super::Tab;
9
10impl Tab {
15 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 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 = escape_for_template_literal(html);
52
53 let script = format!(
54 r#"(function() {{
55 const html = `{}`;
56 const parser = new DOMParser();
57 const doc = parser.parseFromString(html, 'text/html');
58 const newTitle = doc.querySelector('title');
59 if (newTitle) {{ document.title = newTitle.textContent; }}
60 const newBody = doc.body;
61 if (newBody) {{
62 document.body.innerHTML = newBody.innerHTML;
63 for (const attr of newBody.attributes) {{
64 document.body.setAttribute(attr.name, attr.value);
65 }}
66 }}
67 const newHead = doc.head;
68 if (newHead) {{
69 for (const child of newHead.children) {{
70 if (child.tagName !== 'TITLE') {{
71 document.head.appendChild(child.cloneNode(true));
72 }}
73 }}
74 }}
75 }})();"#,
76 escaped_html
77 );
78
79 self.execute_script(&script).await?;
80 Ok(())
81 }
82
83 pub async fn reload(&self) -> Result<()> {
85 debug!(tab_id = %self.inner.tab_id, "Reloading page");
86 let command = Command::BrowsingContext(BrowsingContextCommand::Reload);
87 self.send_command(command).await?;
88 Ok(())
89 }
90
91 pub async fn back(&self) -> Result<()> {
93 debug!(tab_id = %self.inner.tab_id, "Navigating back");
94 let command = Command::BrowsingContext(BrowsingContextCommand::GoBack);
95 self.send_command(command).await?;
96 Ok(())
97 }
98
99 pub async fn forward(&self) -> Result<()> {
101 debug!(tab_id = %self.inner.tab_id, "Navigating forward");
102 let command = Command::BrowsingContext(BrowsingContextCommand::GoForward);
103 self.send_command(command).await?;
104 Ok(())
105 }
106
107 pub async fn get_title(&self) -> Result<String> {
109 debug!(tab_id = %self.inner.tab_id, "Getting page title");
110 let command = Command::BrowsingContext(BrowsingContextCommand::GetTitle);
111 let response = self.send_command(command).await?;
112
113 let title = response
114 .result
115 .as_ref()
116 .and_then(|v| v.get("title"))
117 .and_then(|v| v.as_str())
118 .unwrap_or("")
119 .to_string();
120
121 debug!(tab_id = %self.inner.tab_id, title = %title, "Got page title");
122 Ok(title)
123 }
124
125 pub async fn get_url(&self) -> Result<String> {
127 debug!(tab_id = %self.inner.tab_id, "Getting page URL");
128 let command = Command::BrowsingContext(BrowsingContextCommand::GetUrl);
129 let response = self.send_command(command).await?;
130
131 let url = response
132 .result
133 .as_ref()
134 .and_then(|v| v.get("url"))
135 .and_then(|v| v.as_str())
136 .unwrap_or("")
137 .to_string();
138
139 debug!(tab_id = %self.inner.tab_id, url = %url, "Got page URL");
140 Ok(url)
141 }
142
143 pub async fn focus(&self) -> Result<()> {
145 debug!(tab_id = %self.inner.tab_id, "Focusing tab");
146 let command = Command::BrowsingContext(BrowsingContextCommand::FocusTab);
147 self.send_command(command).await?;
148 Ok(())
149 }
150
151 pub async fn focus_window(&self) -> Result<()> {
153 debug!(tab_id = %self.inner.tab_id, "Focusing window");
154 let command = Command::BrowsingContext(BrowsingContextCommand::FocusWindow);
155 self.send_command(command).await?;
156 Ok(())
157 }
158
159 pub async fn close(&self) -> Result<()> {
161 debug!(tab_id = %self.inner.tab_id, "Closing tab");
162 let command = Command::BrowsingContext(BrowsingContextCommand::CloseTab);
163 self.send_command(command).await?;
164 Ok(())
165 }
166
167 pub async fn get_page_source(&self) -> Result<String> {
169 debug!(tab_id = %self.inner.tab_id, "Getting page source");
170 let result = self
171 .execute_script("return document.documentElement.outerHTML")
172 .await?;
173 Ok(result.as_str().unwrap_or("").to_string())
174 }
175}
176
177fn escape_for_template_literal(s: &str) -> String {
186 let mut out = String::with_capacity(s.len() + s.len() / 8);
187 let mut chars = s.chars().peekable();
188 while let Some(c) = chars.next() {
189 match c {
190 '\\' => out.push_str("\\\\"),
191 '`' => out.push_str("\\`"),
192 '$' if chars.peek() == Some(&'{') => {
193 chars.next(); out.push_str("\\${");
195 }
196 _ => out.push(c),
197 }
198 }
199 out
200}