1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
# SPDX-License-Identifier: GPL-3.0-or-later
"""
Browser profiles and Selenium driver initialization
"""
# This file is part of Haketilo.
#
# Copyright (C) 2021 Wojtek Kosior <koszko@koszko.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# I, Wojtek Kosior, thereby promise not to sue for violation of this file's
# license. Although I request that you do not make use of this code in a
# proprietary program, I am not going to enforce this in court.
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
import json
from shutil import rmtree
from .misc_constants import *
class HaketiloFirefox(webdriver.Firefox):
"""
This wrapper class around selenium.webdriver.Firefox facilitates removing
the temporary profile directory after Firefox quits.
"""
def quit(self, *args, **kwargs):
profile_path = self.firefox_profile.path
super().quit(*args, **kwargs)
rmtree(profile_path, ignore_errors=True)
def set_profile_proxy(profile, proxy_host, proxy_port):
"""
Create a Firefox profile that uses the specified HTTP proxy for all
protocols.
"""
# proxy type 1 designates "manual"
profile.set_preference('network.proxy.type', 1)
profile.set_preference('network.proxy.no_proxies_on', '')
profile.set_preference('network.proxy.share_proxy_settings', True)
for proto in ['http', 'ftp', 'socks', 'ssl']:
profile.set_preference(f'network.proxy.{proto}', proxy_host)
profile.set_preference(f'network.proxy.{proto}_port', proxy_port)
profile.set_preference(f'network.proxy.backup.{proto}', '')
profile.set_preference(f'network.proxy.backup.{proto}_port', 0)
def set_profile_csp_enabled(profile):
"""
By default, Firefox Driver disables CSP. The extension we're testing uses
CSP extensively, so we use this function to prepare a Firefox profile that
has it enabled.
"""
profile.set_preference('security.csp.enable', True)
# The function below seems not to work for extensions that are
# temporarily-installed in Firefox safe mode. Testing is needed to see if it
# works with non-temporary extensions (without safe mode).
def set_webextension_uuid(profile, extension_id, uuid=default_extension_uuid):
"""
Firefox would normally assign a unique, random UUID to installed extension.
This UUID is needed to easily navigate to extension's settings page (and
other extension's pages). Since there's no way to learn such UUID with
current WebDriver implementation, this function works around this by telling
Firefox to use a predefined UUID for a certain extension.
"""
profile.set_preference('extensions.webextensions.uuids',
json.dumps({extension_id: uuid}))
def firefox_safe_mode(firefox_binary=conf_settings['BROWSER_BINARY'],
proxy_host=default_proxy_host,
proxy_port=default_proxy_port):
"""
Initialize a Firefox instance controlled by selenium. The instance is
started in safe mode.
"""
profile = webdriver.FirefoxProfile()
set_profile_proxy(profile, proxy_host, proxy_port)
set_profile_csp_enabled(profile)
options = Options()
options.add_argument('--safe-mode')
return HaketiloFirefox(options=options, firefox_profile=profile,
firefox_binary=firefox_binary)
def firefox_with_profile(firefox_binary=conf_settings['BROWSER_BINARY'],
profile_dir=conf_settings['CLEAN_PROFILE'],
proxy_host=default_proxy_host,
proxy_port=default_proxy_port):
"""
Initialize a Firefox instance controlled by selenium. The instance is
started using an empty profile (either the default one or the one passed to
`configure` script). The empty profile is meant to make Firefox start with
globally-installed extensions disabled.
"""
profile = webdriver.FirefoxProfile(profile_dir)
set_profile_proxy(profile, proxy_host, proxy_port)
set_profile_csp_enabled(profile)
set_webextension_uuid(profile, default_haketilo_id)
return HaketiloFirefox(firefox_profile=profile,
firefox_binary=firefox_binary)
|