Вопрос или проблема
Я создал профиль Firefox, называемый “app”, с парой расширений для скрытия адресной строки и панели вкладок. Моя цель — создать файлы .desktop для Google Inbox, Calendar и т. д., имитирующие функцию “Добавить на рабочий стол” в Chrome. На данный момент у меня есть файлы .desktop, похожие на
#!/usr/bin/env xdg-open
[Desktop Entry]
Version=1.0
Terminal=false
Type=Application
Name=Google Inbox (Firefox)
Exec=firefox -P app -new-window --class googleinbox inbox.google.com
Icon=email
StartupWMClass=googleinbox
Однако, когда у меня есть более одного из них, все окна группируются под значком первого. Когда я использую xprop WM_CLASS
на каждом окне, я обнаруживаю, что у всех действительно WM_CLASS того, которое я открыл первым.
Как можно заставить Firefox обрабатывать флаг -class
для каждого окна, а не для каждого профиля?
Правка: Теперь я использую /usr/bin/firefox -P "PROFILE NAME" --class=WMCLASS
вместо ужасного скрипта, который я привел ниже, и просто сталкиваюсь с необходимостью создавать новые профили для каждого веб-приложения. Кроме того, я использую этот userChrome.css для удаления адресной строки и панели вкладок.
Поскольку никто, похоже, не имеет специфического ответа для Firefox (вероятно, потому, что этот флаг -class
, не будучи задокументированным на странице руководства, не работает должным образом), я просто размещу своё уродливое решение как решение. Если кто-то сможет существенно улучшить его, я дам им ответ.
Я написал следующий скрипт, назвал его firefoxApp.sh и поместил в ~/bin/. Это отвратительный хак, и я стыжусь его. И в то же время горжусь. Я отказался от отдельного профиля Firefox и вместо этого использовал wmctrl, grep и xprop, чтобы изменить WM_CLASS
нового окна после задержки (так как окна иногда требуют значительного времени для появления и получения названия). Если бы кто-то мог подсказать мне более точный и надежный способ найти окна для реклассификации, который не страдает от проблем с синхронизацией, этот скрипт был бы значительно улучшен. Я пытался и не смог сделать это с помощью PID запущенного процесса (предположительно, потому что у всего профиля Firefox один корневой PID). Я не буду размещать здесь код этой попытки, поскольку я его больше не имею.
#!/bin/sh
targetclass=$1
url=$2
titlegrep=$3
if [ "$#" -ne 3 ]
then
echo "USAGE: $0 TARGETCLASS URL TITLEGREP" 1>&2
exit 1
fi
firefox -P default -new-window "$url" &
sleep 10
# Убедитесь, что только переводы строки разделяют элементы в предстоящем цикле:
IFS='
'
for wid in `wmctrl -l -x | grep $titlegrep | awk '{ print \$1 }'`
do
xprop -id $wid -f WM_CLASS 8s -set WM_CLASS $targetclass
done
Этот скрипт я вызываю, используя файлы .desktop в ~/.local/share/applications, которые выглядят следующим образом.
#!/usr/bin/env xdg-open
[Desktop Entry]
Version=1.0
Terminal=false
Type=Application
Name=Calendar (Firefox)
Exec=firefoxApp.sh googlecalendar calendar.google.com Calendar
Icon=calendar
StartupWMClass=googlecalendar
Если я захочу, могу использовать более специфическое имя значка, например google-calendar-firefox-app
, и затем поместить файл с именем google-calendar-firefox-app.svg
в ~/.local/share/icons.
Обновление: Теперь я использую настоятельно отдельные профили для каждого такого “приложения”. Это немного больше работы для настройки, но это можно значительно автоматизировать с помощью другого скрипта который я просто оставлю здесь как Gist, и это работает с Firefox вместо того, чтобы обойти его, уменьшая условия гонок подхода к переименованию окна.
Конечно, значительное недостаток преимущество этого подхода заключается в том, что эти приложения изолированы от основного профиля Firefox и не будут делиться ни одним плагином или расширением с ним. Так, например, вам может понадобиться вручную копировать пароли из LastPass.
Как бонус, скрипт также заполняет файл userChrome.css
, чтобы скрыть хром окон в новом профиле, что способствует созданию иллюзии веб-“приложений”. Его можно вызвать с --help
, чтобы получить
usage: create_firefox_app.py [-h] [--app_name APP_NAME]
[--hide_user_chrome HIDE_USER_CHROME]
[--run_after_creating RUN_AFTER_CREATING]
URL icon_name
позиционные аргументы:
URL Домашняя страница, которая будет использоваться при открытии новых окон в
профиле через файл .desktop.
icon_name Имя значка для использования в файле .desktop. Явный путь
может быть задан, или что-то, что разрешается с использованием
обычного поиска значков (однако это работает; например,
для ~/.local/share/icons/gmail.svg вы можете просто ввести
gmail.svg).
необязательные аргументы:
-h, --help показать это справочное сообщение и выйти
--app_name APP_NAME Имя для созданного "приложения" для использования в файле .desktop.
Если не указано, будет использована версия URL. (по умолчанию: Нет)
--hide_user_chrome HIDE_USER_CHROME
Указывает, нужно ли генерировать файл userChrome.css, который будет скрывать
хром окон во всех окнах, созданных в новом профиле.
Полезно для придания веб-приложениям большей схожести с приложениями.
(по умолчанию: True)
--run_after_creating RUN_AFTER_CREATING
Указывает, нужно ли запускать новое приложение после его создания.
(по умолчанию: True)
Если вы включите опцию hide_user_chrome
и вам нужно будет её вернуть по какой-либо причине, например, чтобы установить расширение, просто удалите сгенерированный файл ~/.mozilla/firefox/PROFILE/chrome/userChrome.css
и перезапустите профиль.
Думаю, это тоже сюда подходит:
Я придумал это чудовище (личное мнение… Я бы хотел, чтобы оно работало с sh 🙂 ) Оно содержит несколько трюков, но довольно надёжно для двух окон.. Вы могли бы также указать другие окна, но они должны быть добавлены вручную, добавив больше "specificprofile1"
вместе с соответствующей функцией.. Может быть, кто-то мог бы сделать это более.. логичным!
#!/bin/bash
# chromium-start.sh $1
# например, укажите:
# chrome-start.sh "Profile 1" в .desktop Exec=
# wmctrl -o 1366,0 ; chromium-browser %U --profile-directory=Profile\ 2 & sleep 3; wmctrl -o 0,0
# $1 = Имя папки профиля
profilename=$1
#2-ой профиль Chromium
specificprofile1="Profile 1"
echo "запуск Chromium"
echo "аргументы: " $1
echo "Имя профиля: " $profilename
echo "Специфический профиль: " $specificprofile1
# Просто устанавливаю переменную масштабирования Chromium, потому что, конечно, разработчики Google не заботятся об отсутствии дробного масштабирования на Linux
scale_var=0.8
# Проверка, существует ли окно Chromium с указанным классом
# Также позволяет использовать значки в качестве "переключателей панели задач" (нажатие на значок переходит в соответствующее окно Chromium)
if wmctrl -l -x | grep "chromium-$profilename"
then
echo "Окно Chromium существует, перемещаем фокус на него"
wmctrl -x -R chromium-"$profilename"
echo "верно"
# Проверка, был ли запущен второй профиль $specifiedprofile1 или нет. Классы WMCLASS должны быть установлены правильно...
elif [[ "$specificprofile1" == "$profilename" ]] && [[ ! "`wmctrl -l -x | grep chromium-"$specificprofile1"`" ]]
then
# TODO: Вложенность
if [ "$specificprofile1" == "$profilename" ]
then
echo $specificprofile1 "равно" $profilename
fi
echo "#2 Окно Chromium для $specificprofile1 не существует"
# wmctrl перемещает в конкретное положение на рабочем столе (1366 означает переход на следующее рабочее пространство, так как моё разрешение 1366x768)
# Будьте осторожны при использовании тайминга сна, так как команда должна иметь достаточно времени для выполнения, чтобы окно находилось в правильном рабочем пространстве
wmctrl -o 1366,0
chromium-browser --profile-directory="$profilename" --force-device-scale-factor=$scale_var %U &
# https://askubuntu.com/a/626524/654028
# Устанавливает последнее открытое окно Chromium, чтобы оно имело пользовательский класс, так как Chromium не заботится о флаге --class=...
# Это имеет свои ограничения, но должно быть достаточно надёжным для большинства использования... Не было протестировано в долгосрочной перспективе.. Что-то, вероятно, может снова сбросить WM_CLASS
# xprop -id "$(wmctrl -l -x| grep "chromium-browser" | tail -n 1 |awk '{ print $1 }')" -f WM_CLASS 8s -set WM_CLASS "chromium-browser.chromium-$specificprofile1"
# Альтернативный метод проверки, существует ли окно с указанным классом
# xprop -id "$(wmctrl -l -x| grep "chromium-$profilename" | tail -n 1 |awk '{ print $1 }')" | grep -o "WM_CLASS(STRING) = ".*"" | grep -o '".*"' | tr -d '"'
# https://stackoverflow.com/a/19441380/5776626
winrep=""
while [[ ! "`echo $winrep | grep -l "Map State: IsViewable"`" ]]
do
winid="$(wmctrl -l -x| grep "chromium-$profilename" | tail -n 1 |awk '{ print $1 }')"
# print $winid
winrep="$(xwininfo -id $winid | grep -o 'Map State: IsViewable')"
# print $winrep
sleep 0.75
xprop -id "$(wmctrl -l -x| grep "chromium-browser" | tail -n 1 |awk '{ print $1 }')" -f WM_CLASS 8s -set WM_CLASS "chromium-browser.chromium-$specificprofile1"
done
# sleep 3
# Перемещение окна непосредственно на рабочее пространство (#2 с разрешением 1366x768 x = 1366), опционально отключите wmctrl -o 1366,0
# wmctrl -v -i -r $winid -e 0,1366,0,-1,-1
# sleep 5
# Возврат на рабочее пространство #1
wmctrl -o 0,0
elif ! wmctrl -l -x | grep chromium-"$profilename"
then
echo "#3 Окно Chromium $profilename не существует"
wmctrl -o 0,0
chromium-browser --profile-directory="$profilename" --force-device-scale-factor=$scale_var %U &
# https://askubuntu.com/a/626524/654028
# ....
# sleep 3
winrep=""
while [[ ! "`echo $winrep | grep -l "Map State: IsViewable"`" ]]
do
winid="$(wmctrl -l -x| grep "chromium-$profilename" | tail -n 1 |awk '{ print $1 }')"
# print $winid
winrep="$(xwininfo -id $winid | grep -o 'Map State: IsViewable')"
# print $winrep
sleep 0.75
xprop -id "$(wmctrl -l -x| grep "chromium-browser" | tail -n 1 |awk '{ print $1 }')" -f WM_CLASS 8s -set WM_CLASS "chromium-browser.chromium-$profilename"
done
wmctrl -o 0,0
# xprop -id "$(wmctrl -l -x| grep "chromium-browser" | tail -n 1 |awk '{ print $1 }')" -f WM_CLASS 8s -set WM_CLASS "chromium-browser.chromium-$profilename"
fi
Проблемы:
При печати возникают ошибки (предупреждения об устаревании..):
Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/%{ <-- HERE (.*?)}/ at /usr/bin/print line 528.
Для отладки вы можете использовать следующее, чтобы выводить ошибки при использовании фактических значков:
https://askubuntu.com/a/664272/654028 (# Ручная альтернатива)
awk '/^Exec=/ {sub("^Exec=", ""); gsub(" ?%[cDdFfikmNnUuv]", ""); exit system($0)}' chrome-ws2.desktop
цикл while зависает, вероятно, из-за интервала цикла
Error: no such file "at while function"
xwininfo: error: -id требует аргумент
xprop: ошибка: Неправильный формат id окна: .
xwininfo: ошибка: -id требует аргумент
xprop: ошибка: Неправильный формат id окна: .
xwininfo: ошибка: -id требует аргумент
xprop: ошибка: Неправильный формат id окна: .
Также при слишком быстром нажатии соответствующего значка .desktop (до того, как пользовательский класс был установлен?), откроется новое окно..
(значительно) Иногда при запуске из обоих Chromium слишком быстро (~<3с), класс ранее открытого окна сбрасывается на chromium-browser.chromium-browser..
Тогда можно ожидать, что значки поменяются местами или будет неожиданное поведение.
Это известная ошибка, исправленная в Firefox 112.
.
Ответ или решение
Для пользователей, стремящихся настроить браузер Firefox так, чтобы он работал подобно Chrome в режиме "Добавить на рабочий стол", существует ряд проблем и решений. Основная цель заключается в том, чтобы использовать Firefox для создания ярлыков приложений, таких как Google Inbox и Calendar, которые визуально и функционально неотличимы от нативных приложений.
Проблема
Возникновение проблемы связано с тем, что Firefox интерпретирует флаг --class
как относящийся ко всему профилю, а не к отдельным окнам. Это приводит к группировке всех окон под один значок в панели задач, что неудобно для пользователей, стремящихся к более точной организации рабочего процесса.
Решение
На момент написания, одним из решений было использование скрипта на Bash, который изменял WM_CLASS
окон Firefox в обход. Однако этот подход подвержен гонкам условий. Озвучу рассмотренное решение:
Скрипт на Bash
Скрипт выполняет следующие шаги:
- Открывает новое окно Firefox с указанным URL.
- Ждет 10 секунд, чтобы окно загружалось.
- Использует утилиты
wmctrl
,grep
, иxprop
для измененияWM_CLASS
нового окна.
#!/bin/sh
targetclass=$1
url=$2
titlegrep=$3
if [ "$#" -ne 3 ]
then
echo "USAGE: $0 TARGETCLASS URL TITLEGREP" 1>&2
exit 1
fi
firefox -P default -new-window "$url" &
sleep 10
IFS='
'
for wid in `wmctrl -l -x | grep $titlegrep | awk '{ print \$1 }'`
do
xprop -id $wid -f WM_CLASS 8s -set WM_CLASS $targetclass
done
Этот скрипт можно вызвать из .desktop
файлов, что позволяет создавать отдельные "псевдо-приложения" на рабочем столе.
Альтернативное решение
Позже была предложена идея создания отдельного профиля для каждого "приложения". Хотя это требует дополнительной настройки, этот метод устраняет проблему гонок условий и обеспечивает большую надежность.
Преимущества изоляции
- Повышенная безопасность: Каждый профиль действует в изоляции, что ограничивает потенциальные угрозы безопасности.
- Настройки и расширения: Хотя профили требуют отдельной установки расширений и плагинов, это позволяет более точно настраивать каждое приложение под конкретные задачи.
Заключение
Важным аспектом является постоянная поддержка и обновления со стороны сообщества Firefox. Вопросы, связанные с WM_CLASS
, были подняты в сообществе разработчиков и фиксируются в более поздних версиях браузера. Важно следить за обновлениями, чтобы иметь доступ к новейшим возможностям и исправлению ошибок.
Используйте предлагаемые решения, чтобы повысить эффективность работы и создать более организованное рабочее окружение, используя Firefox в Linux. Помните, что разработка и поддержка пользователей – это динамичный процесс, который требует адаптации и постоянного поиска инновационных решений.