viewpoint_core/context/scripts/
mod.rs

1//! Context-level init script management.
2
3use tracing::{debug, instrument};
4
5use crate::error::ContextError;
6
7use super::BrowserContext;
8
9impl BrowserContext {
10    /// Add a script to be evaluated before every new page load.
11    ///
12    /// The script will run before any scripts on the page, and will persist
13    /// for all pages created in this context (including popups).
14    ///
15    /// Unlike `page.add_init_script()`, this applies to all pages in the context,
16    /// not just a single page.
17    ///
18    /// # Example
19    ///
20    /// ```ignore
21    /// // Mock navigator.webdriver for all pages
22    /// context.add_init_script(
23    ///     "Object.defineProperty(navigator, 'webdriver', { get: () => false })"
24    /// ).await?;
25    ///
26    /// // All new pages will have this script applied
27    /// let page = context.new_page().await?;
28    /// ```
29    ///
30    /// # Errors
31    ///
32    /// Returns an error if the context is closed.
33    #[instrument(level = "debug", skip(self, script), fields(script_len = script.as_ref().len()))]
34    pub async fn add_init_script(&self, script: impl AsRef<str>) -> Result<(), ContextError> {
35        if self.is_closed() {
36            return Err(ContextError::Closed);
37        }
38
39        let script_content = script.as_ref().to_string();
40        debug!("Adding context-level init script");
41
42        // Store the script for future pages
43        {
44            let mut scripts = self.init_scripts.write().await;
45            scripts.push(script_content.clone());
46        }
47
48        // Apply to existing pages
49        let pages = self.pages.read().await;
50        for page in pages.iter() {
51            if !page.session_id.is_empty() {
52                use viewpoint_cdp::protocol::page::AddScriptToEvaluateOnNewDocumentParams;
53
54                let _ = self.connection()
55                    .send_command::<_, viewpoint_cdp::protocol::page::AddScriptToEvaluateOnNewDocumentResult>(
56                        "Page.addScriptToEvaluateOnNewDocument",
57                        Some(AddScriptToEvaluateOnNewDocumentParams {
58                            source: script_content.clone(),
59                            world_name: None,
60                            include_command_line_api: None,
61                            run_immediately: None,
62                        }),
63                        Some(&page.session_id),
64                    )
65                    .await;
66            }
67        }
68
69        Ok(())
70    }
71
72    /// Add an init script from a file path.
73    ///
74    /// The file contents will be read and registered as an init script for all
75    /// pages in this context.
76    ///
77    /// # Example
78    ///
79    /// ```ignore
80    /// context.add_init_script_path("./scripts/mock-auth.js").await?;
81    /// ```
82    ///
83    /// # Errors
84    ///
85    /// Returns an error if the file cannot be read or the context is closed.
86    #[instrument(level = "debug", skip(self), fields(path = %path.as_ref().display()))]
87    pub async fn add_init_script_path(&self, path: impl AsRef<std::path::Path>) -> Result<(), ContextError> {
88        let content = tokio::fs::read_to_string(path.as_ref()).await.map_err(|e| {
89            ContextError::Internal(format!("Failed to read init script file: {e}"))
90        })?;
91
92        self.add_init_script(&content).await
93    }
94
95    /// Get all context-level init scripts.
96    ///
97    /// This returns the scripts that will be applied to all new pages.
98    pub async fn init_scripts(&self) -> Vec<String> {
99        self.init_scripts.read().await.clone()
100    }
101
102    /// Apply all context-level init scripts to a page session.
103    ///
104    /// This is called internally when a new page is created.
105    pub(crate) async fn apply_init_scripts_to_session(&self, session_id: &str) -> Result<(), ContextError> {
106        let scripts = self.init_scripts.read().await;
107        
108        for script in scripts.iter() {
109            use viewpoint_cdp::protocol::page::AddScriptToEvaluateOnNewDocumentParams;
110
111            self.connection()
112                .send_command::<_, viewpoint_cdp::protocol::page::AddScriptToEvaluateOnNewDocumentResult>(
113                    "Page.addScriptToEvaluateOnNewDocument",
114                    Some(AddScriptToEvaluateOnNewDocumentParams {
115                        source: script.clone(),
116                        world_name: None,
117                        include_command_line_api: None,
118                        run_immediately: None,
119                    }),
120                    Some(session_id),
121                )
122                .await?;
123        }
124
125        Ok(())
126    }
127}