stevepryde/thirtyfour

Add custom parameter to FirefoxProfile?

Closed this issue · 10 comments

Hello @stevepryde I was trying to set a custom User-Agent for geckodriver as I understand to set it we should do the following in python

profile = webdriver.FirefoxProfile()
profile.set_preference("general.useragent.override", "whatever you want")

But as I see there's no such option currently

pub struct FirefoxProfile {
#[serde(rename = "webdriver_accept_untrusted_certs", skip_serializing_if = "Option::is_none")]
pub accept_untrusted_certs: Option<bool>,
#[serde(rename = "webdriver_assume_untrusted_issuer", skip_serializing_if = "Option::is_none")]
pub assume_untrusted_issuer: Option<bool>,
#[serde(rename = "webdriver.log.driver", skip_serializing_if = "Option::is_none")]
pub log_driver: Option<FirefoxProfileLogDriver>,
#[serde(rename = "webdriver.log.file", skip_serializing_if = "Option::is_none")]
pub log_file: Option<String>,
#[serde(rename = "webdriver.load.strategy", skip_serializing_if = "Option::is_none")]
pub load_strategy: Option<String>,
#[serde(rename = "webdriver_firefox_port", skip_serializing_if = "Option::is_none")]
pub webdriver_port: Option<u16>,
}

I could imagine that there's a bigger list of such settings so might a custom parameter not a bad idea?

PS: I didn't test setting of preference

Yeah I think that is a good idea. As for how to adapt what is there, I'm not sure. Feel free to submit a PR if you have an idea of how you'd like to solve it.

@stevepryde
Does FirefoxProfile even work?

Error: An argument passed to the WebDriver server was invalid:     
    Status: 400
    Additional info:
        profile is not a string
        Error: invalid argument
        Stacktrace:

Because this says it only accepts a Base64 encoded string

Fix: #60

@zhiburt
And for the above issue I found this

let mut caps = FirefoxCapabilities::new();
let prefs: serde_json::Value = serde_json::json!({
    "general.useragent.override": "Custom"
});
caps.add_firefox_option("prefs", prefs)?;

Simple test

use anyhow::Result;
use thirtyfour::{FirefoxCapabilities, WebDriver, WebDriverCommands};

#[tokio::main]
pub async fn main() -> Result<()> {
    // User agent string
    let user_agent = "Custom";

    // Set user agent in capabilities
    let mut caps = FirefoxCapabilities::new();
    caps.add_firefox_option(
        "prefs",
        serde_json::json!({ "general.useragent.override": user_agent }),
    )?;

    // Start webdriver and get user agent string
    let c = WebDriver::new("http://localhost:4445", caps).await?;
    c.get("https://www.google.si").await?;
    let js_user_agent: serde_json::Value = c
        .execute_script(r#"return navigator.userAgent;"#)
        .await?
        .convert()?;

    // Test
    assert_eq!(user_agent, &js_user_agent);

    // Exit
    c.close().await?;
    Ok(())
}

Maye an impl like this:

This code goes in Capabilities (This is for adding keys and supporting mutiple nestings)

fn add_key(
    &mut self,
    key: &[&str],
    subkey: &str,
    value: impl Serialize,
) -> WebDriverResult<()> {
    let mut v = self.get_mut();
    let part = &[subkey];
    let mut keys = key.into_iter().chain(part.iter()).peekable();

    // Move to last avaliable key
    while let Some(key) = keys.peek() {
        if v[key].is_null() {
            break;
        }
        v = &mut v[key];
        keys.next();
    }

    // Create missing nested objects
    for key in keys {
        v[key] = json!({});
        v = &mut v[key];
    }

    // Set value
    *v = to_value(value)?;

    Ok(())
}

This code goes in FirefoxCapabilities

fn set_preference(
    &mut self,
    key: &str,
    value: impl Serialize,
) -> WebDriverResult<()> {
    self.add_key(&["moz:firefoxOptions", "prefs"], key, value)
}

Or renaming FirefoxProfile into FirefoxPreferences and having it work the same as Capabilities
AKA having helper methods like set_user_agent(agent: &str), ... and a genric methods like set(key: &str, value impl Serialize), unset(key: &str), ...

Or renaming FirefoxProfile into FirefoxPreferences and having it work the same as Capabilities
AKA having helper methods like set_user_agent(agent: &str), ... and a genric methods like set(key: &str, value impl Serialize), unset(key: &str), ...

I like this option. However I don't have bandwidth for this currently. Would you be interested in making this change?

Thanks so much for this. I've also added your above "test" as examples/firefox_preferences.rs (modified a little to align with other examples).

So I suppose the issue can be closed?

Good job @audioXD