Skip to content

Commit ac9d52f

Browse files
authored
[py] Firefox Profile Fixes and Deprecations (#13477)
* [py] do not change profile preferences of an existing directory by default * [py] deprecate firefox profile methods that are not supported
1 parent 562c359 commit ac9d52f

File tree

1 file changed

+62
-66
lines changed

1 file changed

+62
-66
lines changed

py/selenium/webdriver/firefox/firefox_profile.py

Lines changed: 62 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,19 @@
2828
from io import BytesIO
2929
from xml.dom import minidom
3030

31+
from typing_extensions import deprecated
32+
3133
from selenium.common.exceptions import WebDriverException
3234

3335
WEBDRIVER_PREFERENCES = "webdriver_prefs.json"
34-
EXTENSION_NAME = "[email protected]"
3536

3637

38+
@deprecated("Addons must be added after starting the session")
3739
class AddonFormatError(Exception):
3840
"""Exception for not well-formed add-on manifest files."""
3941

4042

4143
class FirefoxProfile:
42-
ANONYMOUS_PROFILE_NAME = "WEBDRIVER_ANONYMOUS_PROFILE"
4344
DEFAULT_PREFERENCES = None
4445

4546
def __init__(self, profile_directory=None):
@@ -52,61 +53,60 @@ def __init__(self, profile_directory=None):
5253
This defaults to None and will create a new
5354
directory when object is created.
5455
"""
55-
if not FirefoxProfile.DEFAULT_PREFERENCES:
56-
with open(
57-
os.path.join(os.path.dirname(__file__), WEBDRIVER_PREFERENCES), encoding="utf-8"
58-
) as default_prefs:
59-
FirefoxProfile.DEFAULT_PREFERENCES = json.load(default_prefs)
60-
61-
self.default_preferences = copy.deepcopy(FirefoxProfile.DEFAULT_PREFERENCES["mutable"])
62-
self.profile_dir = profile_directory
63-
self.tempfolder = None
64-
if not self.profile_dir:
65-
self.profile_dir = self._create_tempfolder()
66-
else:
67-
self.tempfolder = tempfile.mkdtemp()
68-
newprof = os.path.join(self.tempfolder, "webdriver-py-profilecopy")
56+
self._desired_preferences = {}
57+
if profile_directory:
58+
newprof = os.path.join(tempfile.mkdtemp(), "webdriver-py-profilecopy")
6959
shutil.copytree(
70-
self.profile_dir, newprof, ignore=shutil.ignore_patterns("parent.lock", "lock", ".parentlock")
60+
profile_directory, newprof, ignore=shutil.ignore_patterns("parent.lock", "lock", ".parentlock")
7161
)
72-
self.profile_dir = newprof
73-
os.chmod(self.profile_dir, 0o755)
74-
self._read_existing_userjs(os.path.join(self.profile_dir, "user.js"))
75-
self.extensionsDir = os.path.join(self.profile_dir, "extensions")
76-
self.userPrefs = os.path.join(self.profile_dir, "user.js")
77-
if os.path.isfile(self.userPrefs):
78-
os.chmod(self.userPrefs, 0o644)
62+
self._profile_dir = newprof
63+
os.chmod(self._profile_dir, 0o755)
64+
else:
65+
self._profile_dir = tempfile.mkdtemp()
66+
if not FirefoxProfile.DEFAULT_PREFERENCES:
67+
with open(
68+
os.path.join(os.path.dirname(__file__), WEBDRIVER_PREFERENCES), encoding="utf-8"
69+
) as default_prefs:
70+
FirefoxProfile.DEFAULT_PREFERENCES = json.load(default_prefs)
71+
72+
self._desired_preferences = copy.deepcopy(FirefoxProfile.DEFAULT_PREFERENCES["mutable"])
73+
for key, value in FirefoxProfile.DEFAULT_PREFERENCES["frozen"].items():
74+
self._desired_preferences[key] = value
7975

8076
# Public Methods
8177
def set_preference(self, key, value):
8278
"""Sets the preference that we want in the profile."""
83-
self.default_preferences[key] = value
79+
self._desired_preferences[key] = value
8480

85-
def add_extension(self, extension):
81+
@deprecated("Addons must be added after starting the session")
82+
def add_extension(self, extension=None):
8683
self._install_extension(extension)
8784

8885
def update_preferences(self):
89-
for key, value in FirefoxProfile.DEFAULT_PREFERENCES["frozen"].items():
90-
# Do not update key that is being set by the user using
91-
# set_preference as users are unaware of the freeze properties
92-
# and it leads to an inconsistent behavior
93-
if key not in self.default_preferences:
94-
self.default_preferences[key] = value
95-
self._write_user_prefs(self.default_preferences)
86+
"""Writes the desired user prefs to disk."""
87+
user_prefs = os.path.join(self._profile_dir, "user.js")
88+
if os.path.isfile(user_prefs):
89+
os.chmod(user_prefs, 0o644)
90+
self._read_existing_userjs(user_prefs)
91+
with open(user_prefs, "w", encoding="utf-8") as f:
92+
for key, value in self._desired_preferences.items():
93+
f.write(f'user_pref("{key}", {json.dumps(value)});\n')
9694

9795
# Properties
9896

9997
@property
10098
def path(self):
10199
"""Gets the profile directory that is currently being used."""
102-
return self.profile_dir
100+
return self._profile_dir
103101

104102
@property
103+
@deprecated("The port is stored in the Service class")
105104
def port(self):
106105
"""Gets the port that WebDriver is working on."""
107106
return self._port
108107

109108
@port.setter
109+
@deprecated("The port is stored in the Service class")
110110
def port(self, port) -> None:
111111
"""Sets the port that WebDriver will be running on."""
112112
if not isinstance(port, int):
@@ -121,20 +121,24 @@ def port(self, port) -> None:
121121
self.set_preference("webdriver_firefox_port", self._port)
122122

123123
@property
124+
@deprecated("Allowing untrusted certs is toggled in the Options class")
124125
def accept_untrusted_certs(self):
125-
return self.default_preferences["webdriver_accept_untrusted_certs"]
126+
return self._desired_preferences["webdriver_accept_untrusted_certs"]
126127

127128
@accept_untrusted_certs.setter
129+
@deprecated("Allowing untrusted certs is toggled in the Options class")
128130
def accept_untrusted_certs(self, value) -> None:
129131
if not isinstance(value, bool):
130132
raise WebDriverException("Please pass in a Boolean to this call")
131133
self.set_preference("webdriver_accept_untrusted_certs", value)
132134

133135
@property
136+
@deprecated("Allowing untrusted certs is toggled in the Options class")
134137
def assume_untrusted_cert_issuer(self):
135-
return self.default_preferences["webdriver_assume_untrusted_issuer"]
138+
return self._desired_preferences["webdriver_assume_untrusted_issuer"]
136139

137140
@assume_untrusted_cert_issuer.setter
141+
@deprecated("Allowing untrusted certs is toggled in the Options class")
138142
def assume_untrusted_cert_issuer(self, value) -> None:
139143
if not isinstance(value, bool):
140144
raise WebDriverException("Please pass in a Boolean to this call")
@@ -143,9 +147,10 @@ def assume_untrusted_cert_issuer(self, value) -> None:
143147

144148
@property
145149
def encoded(self) -> str:
146-
"""A zipped, base64 encoded string of profile directory for use with
147-
remote WebDriver JSON wire protocol."""
148-
self.update_preferences()
150+
"""Updates preferences and creates a zipped, base64 encoded string of
151+
profile directory."""
152+
if self._desired_preferences:
153+
self.update_preferences()
149154
fp = BytesIO()
150155
with zipfile.ZipFile(fp, "w", zipfile.ZIP_DEFLATED) as zipped:
151156
path_root = len(self.path) + 1 # account for trailing slash
@@ -155,32 +160,21 @@ def encoded(self) -> str:
155160
zipped.write(filename, filename[path_root:])
156161
return base64.b64encode(fp.getvalue()).decode("UTF-8")
157162

158-
def _create_tempfolder(self):
159-
"""Creates a temp folder to store User.js and the extension."""
160-
return tempfile.mkdtemp()
161-
162-
def _write_user_prefs(self, user_prefs):
163-
"""Writes the current user prefs dictionary to disk."""
164-
with open(self.userPrefs, "w", encoding="utf-8") as f:
165-
for key, value in user_prefs.items():
166-
f.write(f'user_pref("{key}", {json.dumps(value)});\n')
167-
168163
def _read_existing_userjs(self, userjs):
164+
"""Reads existing preferences and adds them to desired preference
165+
dictionary."""
169166
pref_pattern = re.compile(r'user_pref\("(.*)",\s(.*)\)')
170-
try:
171-
with open(userjs, encoding="utf-8") as f:
172-
for usr in f:
173-
matches = pref_pattern.search(usr)
174-
try:
175-
self.default_preferences[matches.group(1)] = json.loads(matches.group(2))
176-
except Exception:
177-
warnings.warn(
178-
f"(skipping) failed to json.loads existing preference: {matches.group(1) + matches.group(2)}"
179-
)
180-
except Exception:
181-
# The profile given hasn't had any changes made, i.e no users.js
182-
pass
167+
with open(userjs, encoding="utf-8") as f:
168+
for usr in f:
169+
matches = pref_pattern.search(usr)
170+
try:
171+
self._desired_preferences[matches.group(1)] = json.loads(matches.group(2))
172+
except Exception:
173+
warnings.warn(
174+
f"(skipping) failed to json.loads existing preference: {matches.group(1) + matches.group(2)}"
175+
)
183176

177+
@deprecated("Addons must be added after starting the session")
184178
def _install_extension(self, addon, unpack=True):
185179
"""Installs addon from a filepath, url or directory of addons in the
186180
profile.
@@ -212,11 +206,12 @@ def _install_extension(self, addon, unpack=True):
212206
assert addon_id, f"The addon id could not be found: {addon}"
213207

214208
# copy the addon to the profile
215-
addon_path = os.path.join(self.extensionsDir, addon_id)
209+
extensions_dir = os.path.join(self._profile_dir, "extensions")
210+
addon_path = os.path.join(extensions_dir, addon_id)
216211
if not unpack and not addon_details["unpack"] and xpifile:
217-
if not os.path.exists(self.extensionsDir):
218-
os.makedirs(self.extensionsDir)
219-
os.chmod(self.extensionsDir, 0o755)
212+
if not os.path.exists(extensions_dir):
213+
os.makedirs(extensions_dir)
214+
os.chmod(extensions_dir, 0o755)
220215
shutil.copy(xpifile, addon_path + ".xpi")
221216
else:
222217
if not os.path.exists(addon_path):
@@ -226,6 +221,7 @@ def _install_extension(self, addon, unpack=True):
226221
if tmpdir:
227222
shutil.rmtree(tmpdir)
228223

224+
@deprecated("Addons must be added after starting the session")
229225
def _addon_details(self, addon_path):
230226
"""Returns a dictionary of details about the addon.
231227

0 commit comments

Comments
 (0)