init
This commit is contained in:
commit
b64a617615
14 files changed
+705
No files matched your search
+78
@@ -0,0 +1,78 @@
|
||||
.idea/
|
||||
bin/
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
*.manifest
|
||||
*.spec
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
*.mo
|
||||
*.pot
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
instance/
|
||||
.webassets-cache
|
||||
.scrapy
|
||||
docs/_build/
|
||||
.pybuilder/
|
||||
target/
|
||||
.ipynb_checkpoints
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
__pypackages__/
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
*.sage.py
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
.spyderproject
|
||||
.spyproject
|
||||
.ropeproject
|
||||
/site
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
.pyre/
|
||||
.pytype/
|
||||
cython_debug/
|
||||
@@ -0,0 +1,3 @@
|
||||
Дополнение, которое дает возможность установить в браузер прокси с авторизацией по логину и паролю, а также дает возможность установить куки без предварительного открытия страницы для которой эти куки предназначены.
|
||||
|
||||
Пример есть в директории example
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Selenium helper for setup proxy and cookies"
|
||||
},
|
||||
"description": {
|
||||
"message": "Setup proxy with authentication and cookies"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Selenium helper"
|
||||
},
|
||||
"description": {
|
||||
"message": "Это расширение дает возможность установить в браузер прокси с авторизацией и куки без предварительного открытия страницы для которой эти куки предназначены."
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 9.6 KiB |
+103
@@ -0,0 +1,103 @@
|
||||
function set_cookies(cookies) {
|
||||
for (let cookie of cookies) {
|
||||
browser.cookies.set(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function set_proxy(PROXY) {
|
||||
// PROXY = {
|
||||
// "type": "http",
|
||||
// "host": "IP address",
|
||||
// "port": "PORT",
|
||||
// "username": "USER NAME",
|
||||
// "password": "PASSWORD"
|
||||
// }
|
||||
const TARGET = "<all_urls>";
|
||||
const CREDENTIALS = {
|
||||
username: PROXY.username,
|
||||
password: PROXY.password
|
||||
}
|
||||
const pendingRequests = [];
|
||||
|
||||
function handleProxyRequest(requestInfo) {
|
||||
return {type: PROXY.type, host: PROXY.host, port: PROXY.port};
|
||||
}
|
||||
|
||||
// A request has completed.
|
||||
// We can stop worrying about it.
|
||||
function completed(requestDetails) {
|
||||
var index = pendingRequests.indexOf(requestDetails.requestId);
|
||||
if (index > -1) {
|
||||
pendingRequests.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function provideCredentialsSync(requestDetails) {
|
||||
// If we have seen this request before, then
|
||||
// assume our credentials were bad, and give up.
|
||||
if (pendingRequests.indexOf(requestDetails.requestId) != -1) {
|
||||
console.log(`bad credentials for: ${requestDetails.requestId}`);
|
||||
return {cancel: true};
|
||||
}
|
||||
pendingRequests.push(requestDetails.requestId);
|
||||
console.log(`providing credentials for: ${requestDetails.requestId}`);
|
||||
return {authCredentials: CREDENTIALS};
|
||||
}
|
||||
|
||||
browser.proxy.onRequest.addListener(
|
||||
handleProxyRequest,
|
||||
{urls: [TARGET]}
|
||||
);
|
||||
|
||||
browser.webRequest.onAuthRequired.addListener(
|
||||
provideCredentialsSync,
|
||||
{urls: [TARGET]},
|
||||
["blocking"]
|
||||
);
|
||||
|
||||
browser.webRequest.onCompleted.addListener(
|
||||
completed,
|
||||
{urls: [TARGET]}
|
||||
);
|
||||
|
||||
browser.webRequest.onErrorOccurred.addListener(
|
||||
completed,
|
||||
{urls: [TARGET]}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function main() {
|
||||
|
||||
function setProxy(result) {
|
||||
if (result.proxy) {
|
||||
console.log("Setup proxy...");
|
||||
set_proxy(result.proxy);
|
||||
}
|
||||
}
|
||||
|
||||
function setCookies(result) {
|
||||
if (result.cookies) {
|
||||
console.log("Setup cookies...");
|
||||
set_cookies(result.cookies);
|
||||
}
|
||||
}
|
||||
|
||||
function onError(error) {
|
||||
console.log(`Error: ${error}`);
|
||||
}
|
||||
|
||||
let proxy = browser.storage.sync.get("proxy");
|
||||
proxy.then(setProxy, onError);
|
||||
|
||||
let cookies = browser.storage.sync.get("cookies");
|
||||
cookies.then(setCookies, onError);
|
||||
}
|
||||
|
||||
|
||||
browser.runtime.onMessage.addListener(function(message, sender, reply) {
|
||||
if (message.update) {
|
||||
main();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
</head>
|
||||
<body>
|
||||
<form id="options_form" method="POST">
|
||||
<input type="text" name="type" id="type">
|
||||
<input type="text" name="host" id="host">
|
||||
<input type="text" name="port" id="port">
|
||||
<input type="text" name="username" id="username">
|
||||
<input type="text" name="password" id="password">
|
||||
<input id="submit" type="submit" value="Submit">
|
||||
<textarea id="cookies" name="cookies"></textarea>
|
||||
</form>
|
||||
<script src="options.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,58 @@
|
||||
function saveOptions(e) {
|
||||
e.preventDefault();
|
||||
let cookies = [];
|
||||
try {
|
||||
cookies = JSON.parse(document.getElementById("cookies").value);
|
||||
} catch {};
|
||||
console.log(cookies)
|
||||
browser.storage.sync.set({
|
||||
proxy: {
|
||||
"type": document.getElementById("type").value,
|
||||
"host": document.getElementById("host").value,
|
||||
"port": document.getElementById("port").value,
|
||||
"username": document.getElementById("username").value,
|
||||
"password": document.getElementById("password").value
|
||||
},
|
||||
cookies: cookies
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function restoreOptions() {
|
||||
|
||||
function setCurrentProxy(result) {
|
||||
if (result.proxy) {
|
||||
document.getElementById("type").value = result.proxy.type;
|
||||
document.getElementById("host").value = result.proxy.host;
|
||||
document.getElementById("port").value = result.proxy.port;
|
||||
document.getElementById("username").value = result.proxy.username;
|
||||
document.getElementById("password").value = result.proxy.password;
|
||||
}
|
||||
}
|
||||
|
||||
function setCurrentCookies(result) {
|
||||
if (result.cookies) {
|
||||
document.getElementById("cookies").value = JSON.stringify(result.cookies);
|
||||
}
|
||||
}
|
||||
|
||||
function onError(error) {
|
||||
console.log(`Error: ${error}`);
|
||||
}
|
||||
|
||||
let proxy = browser.storage.sync.get("proxy");
|
||||
proxy.then(setCurrentProxy, onError);
|
||||
|
||||
let cookies = browser.storage.sync.get("cookies");
|
||||
cookies.then(setCurrentCookies, onError);
|
||||
}
|
||||
|
||||
|
||||
function notifyExtension(e) {
|
||||
browser.runtime.sendMessage({"update": true});
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener("DOMContentLoaded", restoreOptions);
|
||||
document.getElementById("options_form").addEventListener("submit", saveOptions);
|
||||
window.addEventListener("submit", notifyExtension);
|
||||
@@ -0,0 +1,40 @@
|
||||
import logging
|
||||
import os.path
|
||||
|
||||
import selenium.webdriver
|
||||
|
||||
import selenium_helper
|
||||
|
||||
|
||||
def main():
|
||||
logger = logging.getLogger(__name__)
|
||||
profile = selenium.webdriver.FirefoxProfile()
|
||||
# отключаем предупреждение при открытии about:config
|
||||
profile.set_preference("browser.aboutConfig.showWarning", False)
|
||||
# отключаем трекинг navigator.webdriver
|
||||
profile.set_preference("dom.webdriver.enabled", False)
|
||||
# отключаем уведомления от сайтов
|
||||
profile.set_preference("dom.webnotifications.enabled", False)
|
||||
|
||||
driver = selenium.webdriver.Firefox(
|
||||
firefox_binary="bin/firefox-84.0.2/firefox-bin", executable_path="bin/geckodriver", firefox_profile=profile)
|
||||
|
||||
try:
|
||||
selenium_helper_extention = selenium_helper.SeleniumHelperExtension(driver)
|
||||
selenium_helper_extention.install(os.path.abspath("releases/selenium_helper-0.0.4-fx.xpi"))
|
||||
proxy = "http://username:password@host:port"
|
||||
cookies = [
|
||||
selenium_helper.Cookie(name="cookie", value="test", url="https://whoer.net")
|
||||
]
|
||||
selenium_helper_extention.begin_options().set_proxy(proxy).set_cookies(cookies).apply_options()
|
||||
|
||||
driver.get("https://whoer.net/")
|
||||
input("Enter to exit...")
|
||||
except Exception as e:
|
||||
logger.exception(f"{type(e).__name__}{getattr(e, 'args', None)}")
|
||||
finally:
|
||||
driver.quit()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,350 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Created at 19.01.2021.
|
||||
# Python 3.7.3 x64
|
||||
# Contacts: alexandrsokolov@cock.li
|
||||
#
|
||||
import copy
|
||||
import json
|
||||
import urllib.parse
|
||||
from functools import lru_cache
|
||||
from typing import List
|
||||
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
from selenium.webdriver import Firefox
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
|
||||
OPTION_PROXY_TYPE = "proxy_type"
|
||||
OPTION_PROXY_HOST = "proxy_host"
|
||||
OPTION_PROXY_PORT = "proxy_port"
|
||||
OPTION_PROXY_USERNAME = "proxy_username"
|
||||
OPTION_PROXY_PASSWORD = "proxy_password"
|
||||
OPTION_PROXY_COOKIES = "proxy_cookies"
|
||||
|
||||
|
||||
class NoAddonException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CookieException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Cookie(dict):
|
||||
"""https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/cookies/set"""
|
||||
|
||||
def __init__(self, *, domain=None, expiration_date=None, first_party_domain=None, http_only=None, name=None,
|
||||
path=None, same_site=None, secure=None, store_id=None, url=None, value=None):
|
||||
super().__init__()
|
||||
if domain:
|
||||
self.domain = domain
|
||||
if expiration_date:
|
||||
self.expiration_date = expiration_date
|
||||
if first_party_domain:
|
||||
self.first_party_domain = first_party_domain
|
||||
if http_only:
|
||||
self.http_only = http_only
|
||||
if name:
|
||||
self.name = name
|
||||
if path:
|
||||
self.path = path
|
||||
if same_site:
|
||||
self.same_site = same_site
|
||||
if secure:
|
||||
self.secure = secure
|
||||
if store_id:
|
||||
self.store_id = store_id
|
||||
if url:
|
||||
self.url = url
|
||||
if value:
|
||||
self.value = value
|
||||
|
||||
@property
|
||||
def domain(self):
|
||||
"""A string representing the domain of the cookie. If omitted, the cookie becomes a host-only cookie."""
|
||||
return self.get("domain")
|
||||
|
||||
@domain.setter
|
||||
def domain(self, value):
|
||||
self["domain"] = value
|
||||
|
||||
@domain.deleter
|
||||
def domain(self):
|
||||
del self["domain"]
|
||||
|
||||
@property
|
||||
def expiration_date(self):
|
||||
"""A number that represents the expiration date of the cookie as the number of seconds since the UNIX epoch.
|
||||
If omitted, the cookie becomes a session cookie."""
|
||||
return self.get("expirationDate")
|
||||
|
||||
@expiration_date.setter
|
||||
def expiration_date(self, value):
|
||||
self["expirationDate"] = value
|
||||
|
||||
@expiration_date.deleter
|
||||
def expiration_date(self):
|
||||
del self["expirationDate"]
|
||||
|
||||
@property
|
||||
def first_party_domain(self):
|
||||
"""A string representing the first-party domain with which the cookie to will be associated.
|
||||
This property must be supplied if the browser has first-party isolation enabled. See First-party isolation
|
||||
https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/cookies#First-party_isolation"""
|
||||
return self.get("firstPartyDomain")
|
||||
|
||||
@first_party_domain.setter
|
||||
def first_party_domain(self, value):
|
||||
self["firstPartyDomain"] = value
|
||||
|
||||
@first_party_domain.deleter
|
||||
def first_party_domain(self):
|
||||
del self["firstPartyDomain"]
|
||||
|
||||
@property
|
||||
def http_only(self):
|
||||
"""A boolean that specifies whether the cookie should be marked as HttpOnly (true), or not (false).
|
||||
If omitted, it defaults to false."""
|
||||
return self.get("httpOnly")
|
||||
|
||||
@http_only.setter
|
||||
def http_only(self, value):
|
||||
self["httpOnly"] = value
|
||||
|
||||
@http_only.deleter
|
||||
def http_only(self):
|
||||
del self["httpOnly"]
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""A string representing the name of the cookie. If omitted, this is empty by default."""
|
||||
return self.get("name")
|
||||
|
||||
@name.setter
|
||||
def name(self, value):
|
||||
self["name"] = value
|
||||
|
||||
@name.deleter
|
||||
def name(self):
|
||||
del self["name"]
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""A string representing the path of the cookie.
|
||||
If omitted, this defaults to the path portion of the URL parameter."""
|
||||
return self.get("path")
|
||||
|
||||
@path.setter
|
||||
def path(self, value):
|
||||
self["path"] = value
|
||||
|
||||
@path.deleter
|
||||
def path(self):
|
||||
del self["path"]
|
||||
|
||||
@property
|
||||
def same_site(self):
|
||||
"""A cookies.SameSiteStatus value that indicates the SameSite state of the cookie.
|
||||
If omitted, it defaults to 0, 'no_restriction'.
|
||||
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/cookies/SameSiteStatus"""
|
||||
return self.get("sameSite")
|
||||
|
||||
@same_site.setter
|
||||
def same_site(self, value):
|
||||
self["sameSite"] = value
|
||||
|
||||
@same_site.deleter
|
||||
def same_site(self):
|
||||
del self["sameSite"]
|
||||
|
||||
@property
|
||||
def secure(self):
|
||||
"""A boolean that specifies whether the cookie should be marked as secure (true), or not (false).
|
||||
If omitted, it defaults to false."""
|
||||
return self.get("secure")
|
||||
|
||||
@secure.setter
|
||||
def secure(self, value):
|
||||
self["secure"] = value
|
||||
|
||||
@secure.deleter
|
||||
def secure(self):
|
||||
del self["secure"]
|
||||
|
||||
@property
|
||||
def store_id(self):
|
||||
"""A string representing the ID of the cookie store in which to set the cookie.
|
||||
If omitted, the cookie is set in the current execution context's cookie store by default."""
|
||||
return self.get("storeId")
|
||||
|
||||
@store_id.setter
|
||||
def store_id(self, value):
|
||||
self["storeId"] = value
|
||||
|
||||
@store_id.deleter
|
||||
def store_id(self):
|
||||
del self["storeId"]
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
"""A string representing the request-URI to associate with the cookie.
|
||||
This value can affect the default domain and path values of the created cookie."""
|
||||
return self.get("url")
|
||||
|
||||
@url.setter
|
||||
def url(self, value):
|
||||
self["url"] = value
|
||||
|
||||
@url.deleter
|
||||
def url(self):
|
||||
del self["url"]
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
"""A string representing the value of the cookie.
|
||||
If omitted, this is empty by default."""
|
||||
return self.get("value")
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
self["value"] = value
|
||||
|
||||
@value.deleter
|
||||
def value(self):
|
||||
del self["value"]
|
||||
|
||||
|
||||
class SeleniumHelperExtension:
|
||||
"""Обертка для установки и настройки дополнения"""
|
||||
|
||||
_uuid = None
|
||||
|
||||
_options = {
|
||||
OPTION_PROXY_TYPE: "type",
|
||||
OPTION_PROXY_HOST: "host",
|
||||
OPTION_PROXY_PORT: "port",
|
||||
OPTION_PROXY_USERNAME: "username",
|
||||
OPTION_PROXY_PASSWORD: "password",
|
||||
OPTION_PROXY_COOKIES: "cookies"
|
||||
}
|
||||
|
||||
def __init__(self, driver: Firefox, raise_for_about_config=True):
|
||||
self.raise_for_about_config = raise_for_about_config
|
||||
self._driver = driver
|
||||
|
||||
@property
|
||||
def uuid(self):
|
||||
return self._uuid
|
||||
|
||||
@property
|
||||
def internal_uuid(self):
|
||||
installed_addons = json.loads(self._get_preference("extensions.webextensions.uuids"))
|
||||
try:
|
||||
return installed_addons[self.uuid]
|
||||
except KeyError:
|
||||
raise NoAddonException("Addon `selenium_helper` not found. Is it installed?")
|
||||
|
||||
@property
|
||||
def setup_url(self):
|
||||
return f"moz-extension://{self.internal_uuid}/data/options.html"
|
||||
|
||||
def set_uuid(self, uuid: str):
|
||||
"""если дополнение уже установлено, требуется его UUID для того чтобы найти дополнение в списке
|
||||
установленных и получить его внутренний UUID"""
|
||||
self._uuid = uuid
|
||||
|
||||
def install_debug(self):
|
||||
"""открываем страницу отладки дополнений и предоставляем возможность установить дополнение из файла"""
|
||||
self._driver.get("about:debugging#/runtime/this-firefox")
|
||||
if self._uuid:
|
||||
input("Install extension from file and press Enter to continue...")
|
||||
else:
|
||||
self._uuid = input("Install extension from file and enter UUID:")
|
||||
|
||||
def install(self, xpi_path: str):
|
||||
"""установка дополнения в браузер"""
|
||||
self._uuid = self._driver.install_addon(xpi_path)
|
||||
|
||||
def begin_options(self):
|
||||
"""открываем настройки дополнения"""
|
||||
self._driver.get(self.setup_url)
|
||||
return self
|
||||
|
||||
def set_proxy(self, proxy_string: str):
|
||||
"""заполняем поля для прокси"""
|
||||
proxy = urllib.parse.urlparse(proxy_string)
|
||||
self._fill_option(OPTION_PROXY_TYPE, proxy.scheme)
|
||||
self._fill_option(OPTION_PROXY_HOST, proxy.hostname)
|
||||
self._fill_option(OPTION_PROXY_PORT, proxy.port)
|
||||
self._fill_option(OPTION_PROXY_USERNAME, proxy.username)
|
||||
self._fill_option(OPTION_PROXY_PASSWORD, proxy.password)
|
||||
return self
|
||||
|
||||
def set_cookies(self, cookies: List[Cookie], default_url=None, ignore_errors=False):
|
||||
"""устанавливаем куки. у кук должен быть url, если его нет, то попытаемся создать его из полей domain и path,
|
||||
если это не получится сделать, то будем использовать переменную default_url, если она не установлена,
|
||||
то выбрасываем исключение, а если ignore_errors установлен в True, то пропускаем проблемную куку."""
|
||||
clear_value = self._clear_cookies(cookies, default_url, ignore_errors)
|
||||
|
||||
value = json.dumps(clear_value)
|
||||
|
||||
self._fill_option(OPTION_PROXY_COOKIES, value)
|
||||
return self
|
||||
|
||||
def apply_options(self):
|
||||
"""сохраняем изменения сделанные на странице настроек"""
|
||||
self._driver.find_element_by_id("submit").click()
|
||||
|
||||
def _clear_cookies(self, cookies: List[Cookie], default_url=None, ignore_errors=False):
|
||||
clear_value = []
|
||||
|
||||
for cookie in copy.deepcopy(cookies):
|
||||
if not cookie.get("url"):
|
||||
try:
|
||||
domain = cookie["domain"][1:] if cookie["domain"].startswith(".") else cookie["domain"]
|
||||
url = f"http://{domain}{cookie.get('path', '/')}"
|
||||
except KeyError:
|
||||
url = default_url
|
||||
|
||||
if url is None:
|
||||
if ignore_errors:
|
||||
continue
|
||||
else:
|
||||
raise CookieException(f"No url specified for cookie {cookie}")
|
||||
|
||||
cookie["url"] = url
|
||||
clear_value.append(cookie)
|
||||
return clear_value
|
||||
|
||||
def _fill_option(self, name, value):
|
||||
self._fill_element(self._driver.find_element_by_id(self._options[name]), value)
|
||||
|
||||
@lru_cache()
|
||||
def _get_preference(self, name):
|
||||
|
||||
def get_search_box_with_wait_about_config_approved():
|
||||
try:
|
||||
search_box = self._driver.find_element_by_id("about-config-search") # noqa
|
||||
except NoSuchElementException:
|
||||
if self.raise_for_about_config:
|
||||
raise
|
||||
else:
|
||||
input("Approve warning message and press Enter to continue...")
|
||||
return get_search_box_with_wait_about_config_approved()
|
||||
else:
|
||||
return search_box
|
||||
|
||||
self._driver.get("about:config")
|
||||
|
||||
search_box = get_search_box_with_wait_about_config_approved()
|
||||
search_box.clear()
|
||||
search_box.send_keys(name)
|
||||
search_box.send_keys(Keys.ENTER)
|
||||
|
||||
search_result = self._driver.find_elements_by_xpath("//table[@id='prefs']/tr/td/span")
|
||||
return search_result[0].text
|
||||
|
||||
def _fill_element(self, element, value: str): # noqa
|
||||
element.clear()
|
||||
element.send_keys(value)
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"version": "0.0.4",
|
||||
"minimum_chrome_version": "76.0",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "eet1ra8cauDu@AlexandrSokolov"
|
||||
}
|
||||
},
|
||||
"default_locale": "en",
|
||||
"name": "__MSG_name__",
|
||||
"short_name": "__MSG_name__",
|
||||
"description": "__MSG_description__",
|
||||
"author": "Alexandr Sokolov",
|
||||
"homepage_url": "https://github.com/alexsok-bit/selenium_helper",
|
||||
"icons": {
|
||||
"32": "app32.png",
|
||||
"64": "app64.png",
|
||||
"128": "app128.png"
|
||||
},
|
||||
"permissions": [
|
||||
"proxy",
|
||||
"storage",
|
||||
"cookies",
|
||||
"webRequest",
|
||||
"webRequestBlocking",
|
||||
"<all_urls>"
|
||||
],
|
||||
"options_ui": {
|
||||
"page": "data/options.html"
|
||||
},
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "eet1ra8cauDu@AlexandrSokolov"
|
||||
}
|
||||
},
|
||||
"background": {
|
||||
"scripts": ["data/content.js"]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Reference in new issue
Block a user