Repository: robotframework/SeleniumLibrary

Issue:
Add better support for FireFox Profile class
Currently `Open Browser` keywords supports Firefox profile by taking in a string which points to the Firefox profile in the file system. Either users creates a profile by using Firefox and saving the profile with their test automation code or the use Python and use [FireFox FirefoxProfile](https://seleniumhq.github.io/selenium/docs/api/py/webdriver_firefox/selenium.webdriver.firefox.firefox_profile.html#selenium.webdriver.firefox.firefox_profile.FirefoxProfile) class to create new profile. 

But it might be better to allow users to create a Firefox profile with the `Open Browser` keyword in a SeleniumLibrary. There are few usage cases which needs support: 
1. Allow use existing profile as a base: Same as `FirefoxProfile('/path/to/profile_dir')`
2. Allow to extension with [add_extension](https://seleniumhq.github.io/selenium/docs/api/py/webdriver_firefox/selenium.webdriver.firefox.firefox_profile.html#selenium.webdriver.firefox.firefox_profile.FirefoxProfile.add_extension) method
3. Allow to [set preferences](https://seleniumhq.github.io/selenium/docs/api/py/webdriver_firefox/selenium.webdriver.firefox.firefox_profile.html#selenium.webdriver.firefox.firefox_profile.FirefoxProfile.set_preference)
4. Allow [adding proxy](https://seleniumhq.github.io/selenium/docs/api/py/webdriver_firefox/selenium.webdriver.firefox.firefox_profile.html#selenium.webdriver.firefox.firefox_profile.FirefoxProfile.set_proxy)
5. Support also FirefoxProfile instance. 

keywords signature would be something like this: 
```
| ${profile} = | Open Browser |  https://robotframework.org | Firefox | profile_dir=add_extension:/path/to/ext1,add_extension:/path/to/ext2 | 
```

This issue is splitting the work of the #701


Discussion:
aaltat: To fully fix the #1332 needs #1393 because they are sharing part of the code.

Patch:
diff --git a/src/SeleniumLibrary/keywords/browsermanagement.py b/src/SeleniumLibrary/keywords/browsermanagement.py
index 355d0cea..9d4c6d89 100644
--- a/src/SeleniumLibrary/keywords/browsermanagement.py
+++ b/src/SeleniumLibrary/keywords/browsermanagement.py
@@ -115,7 +115,9 @@ class BrowserManagementKeywords(LibraryComponent):
         Optional ``ff_profile_dir`` is the path to the Firefox profile
         directory if you wish to overwrite the default profile Selenium
         uses. Notice that prior to SeleniumLibrary 3.0, the library
-        contained its own profile that was used by default.
+        contained its own profile that was used by default. The
+        ``ff_profile_dir`` can also be instance of the
+        [https://seleniumhq.github.io/selenium/docs/api/py/webdriver_firefox/selenium.webdriver.firefox.firefox_profile.html?highlight=firefoxprofile#selenium.webdriver.firefox.firefox_profile.FirefoxProfile|selenium.webdriver.FirefoxProfile].
 
         Optional ``service_log_path`` argument defines the name of the
         file where to write the browser driver logs. If the
@@ -149,7 +151,9 @@ class BrowserManagementKeywords(LibraryComponent):
 
         Using ``alias`` to decide, is the new browser opened is new
         in SeleniumLibrary 4.0. Also the ``service_log_path`` is new
-        in SeleniumLibrary 4.0.
+        in SeleniumLibrary 4.0. Support for ``ff_profile_dir`` accepting
+        instance of the `selenium.webdriver.FirefoxProfile` is new in
+        SeleniumLibrary 4.0.
         """
         index = self.drivers.get_index(alias)
         if index:
diff --git a/src/SeleniumLibrary/keywords/webdrivertools.py b/src/SeleniumLibrary/keywords/webdrivertools.py
index 23ab2fbe..68c3e584 100644
--- a/src/SeleniumLibrary/keywords/webdrivertools.py
+++ b/src/SeleniumLibrary/keywords/webdrivertools.py
@@ -20,6 +20,7 @@ import warnings
 from robot.api import logger
 from robot.utils import ConnectionCache
 from selenium import webdriver
+from selenium.webdriver import FirefoxProfile
 
 from SeleniumLibrary.utils import is_falsy, is_truthy, is_noney
 
@@ -122,6 +123,8 @@ class WebDriverCreator(object):
                                  **desired_capabilities)
 
     def _get_ff_profile(self, ff_profile_dir):
+        if isinstance(ff_profile_dir, FirefoxProfile):
+            return ff_profile_dir
         if is_falsy(ff_profile_dir):
             return webdriver.FirefoxProfile()
         return webdriver.FirefoxProfile(ff_profile_dir)


Test patch:
diff --git a/utest/test/keywords/test_webdrivercreator.py b/utest/test/keywords/test_webdrivercreator.py
index 65bb0eba..e2afbcbf 100644
--- a/utest/test/keywords/test_webdrivercreator.py
+++ b/utest/test/keywords/test_webdrivercreator.py
@@ -186,6 +186,24 @@ class WebDriverCreatorTests(unittest.TestCase):
         self.assertEqual(driver, expected_webdriver)
         verify(webdriver).FirefoxProfile()
 
+    def test_get_ff_profile_real_path(self):
+        profile_path = '/path/to/profile'
+        profile_mock = mock()
+        when(webdriver).FirefoxProfile(profile_path).thenReturn(profile_mock)
+        profile = self.creator._get_ff_profile(profile_path)
+        self.assertEqual(profile, profile_mock)
+
+    def test_get_ff_profile_no_path(self):
+        profile_mock = mock()
+        when(webdriver).FirefoxProfile().thenReturn(profile_mock)
+        profile = self.creator._get_ff_profile(None)
+        self.assertEqual(profile, profile_mock)
+
+    def test_get_ff_profile_instance_FirefoxProfile(self):
+        input_profile = webdriver.FirefoxProfile()
+        profile = self.creator._get_ff_profile(input_profile)
+        self.assertEqual(profile, input_profile)
+
     def test_firefox_remote_no_caps(self):
         url = 'http://localhost:4444/wd/hub'
         profile = mock()