Добрый день!
Меня зовут Тимур и я программист.
Сегодня я хочу представить очередную свою работу, точнее рабочий черновик. Да, я в курсе — дураку полработы не показывают, но так и на хабре то не дураки сидят. Итак, сегодня я расскажу как перенес поддержку webextensions в chromium под Андроид, что работает а что не работает, как пользоваться тем что работает и когда будет работать то что не работает.
Обзор
Код доступен тут, лицензия BSD, все по честному.
Немного предистории. Браузеров на базе хромиума с поддержкой расширений на андроид есть на самом деле и не один.
- Microsoft Edge — поддержка экспериментальная, включена (пока) только в сборке Canary, для установки нужно знать id расширения (и ставится оно само собой только с микрософтовского стора). Тут обсуждение на reddit а здесь список поддерживаемых и не поддерживаемых api. Код закрыт.
- Яндекс браузер — расширения можно ставить как со стора Яндекса так и с Оперы и Хрома. Список поддерживаемых api я не смог найти, если кто в курсе — дайте знать. Тут официальная информация от Яндекса. Код закрыт.
- Samsung Internet Заявлена поддержка, но списка доступных api нет, установка с Galaxy Store, девелоперская программа закрытая, нужны дополнительные телодвижения что бы в ней поучаствовать. Код закрытый. Мне он предложил на выбор 6 блокировщиков рекламы, ничего больше я найти не смог, может быть санкции, может быть из за того что мобила порутаная, если у кого другой опыт — пишите, добавлю в статью.
- Kiwi браузер. Проект закрыт (похоже что куплен Microsoft). Разработчик уверял что проект opensource но по факту со 105-ой версии код не выкладывался, на 105-ой версии выложенный код — не рабочий. (вот тут я смерджил весь имеющийся код kiwi чтобы его можно было посмотреть одним коммитом). На Хабре была новость об этом, там же в обсуждении есть ссылка на мое общение с разрабом (точнее его адьютантом, сам разраб так и не озвучился). Но справедливости ради — по отзывам пользователей у браузера была наиболее полная поддержка расширений, правда только manifest v2, поддержка manifest v3 была завлена только в последнем прощальном билде, 132-ая версия (но списка api опять же нет). RIP.
- Quetta Заявлена поддержка расширений, устанавливать можно с Chrome Web Store и Microsoft Add-ons. Списка доступных api также не нашел, в сети есть информация о том что код утянут с проекта Kiwi, разраб обещал открыть код но это было больше чем год назад. Пользователи жалуются что браузер агрессивно собирает информацию, насколько это соответствует — не могу сказать, не проверял.
- Mises Browser в отличие от kiwi реально похож на opensource. Я его не собирал (после того как на kiwi было потрачено три полных дня я плюнул и решил что впилю поддержку сам и с нуля) но походив по коду сложилось впечатление вполне себе живого проекта. К сожалению лицензия не free а copyleft, но тем не менее на данный момент это единственный проект с поддержкой расширений И открытым кодом (ну не считая Ultimatum, само собой). Очень много кода (не связанного с расширениями) взято с brave (что объясняет лицензию). Основной упор в проекте — на поддержку криптокошельков, так что поддержку расширений оттуда надо еще вычленить и это может оказаться нетривиальной задачей. Единственное что могу добавить — поддержку тем (themes) и части ресурсов (css, иконки) для webui хромиума они не осилили, это видно когда на установленном браузере заходишь в расширения — в левом верхнем углу нет иконки. Я тоже с этим столкнулся и для того что бы добавить эту поддержку мне пришлось делать отдельный подход, заняло почти день.
Как видим ситуация у нас привычная, выборы есть а выбора нет.
Тем кто пропустил мои предыдущие статьи, коротенько — я занимаюсь форком chromium, назвал его Ultimatum, анонсы и статьи по теме — у меня в профиле. И вот после очередного своего захода в код, когда я поднял это все до 132-ой версии chromium-а, исправил баг с disk_cache который не позволял делать параллельные запросы к кешам и переписал Помогатор с учетом этих фиксов — настал момент подумать куда двигаться дальше.
Похождения
Народ который пользуется моими сборками подсказал мне что очень сильно хотели бы увидеть Помогатор на мобилах — это позволило бы им работать со скидками на продуктовых сетках (там своя кухня, я туда особо не лезу, основная идея в том что ради скидки в 500 рублей никто в магазин с ноутбуком не пойдет, а если это будет на мобиле то почему бы не достать ее из кармана и не сэкономить 500 рублей?)
Ok, подумал я и пошел смотреть что у нас есть в дикой природе (тот самый списочек в начале статьи). Потратив три дня на kiwi и узнав что kiwi не собирается не потому что выложенный код не рабочий а потому что у меня скилов не хватает его собрать я решил перестать тратить время на странных людей и выкатить свой код который гарантированно будет рабочим и открытым.
По времени это все удачно совпало с тем что гугль незадолго до этого решил поэкспериментировать с поддержкой расширений на андроид десктопах (что бы это ни значило, полагаю это что-то про планшеты), вот тут что-то вроде анонса. То есть как минимум хотя бы какая-то часть кода под андроид уже собирается и это уже неплохое начало дня. На самом деле это не сказать что прям сильно облегчило задачу, просто дало надежду скорее.
Первым моим порывом было указать в конфиге сборки enable_extensions и собрать как оно обычно собирается под android. Естественно по пути будут ошибки, какой-то код будет сопротивляться а я буду приходить и уговаривать его работать. После того как эта блестящая идея пришла мне в голову я, не теряя времени, взялся за ее реализацию. К вечеру второго дня, когда сообщения о ошибках компиляции уже не помещались на три-четыре экрана, git status показывал мне несколько экранов затронутых файлов а в голове постепенно зрело понимание того что это мы еще до линковки не дошли и сколько ошибок будет на линковке мы можем только гадать — я начал подозревать неладное. К тому моменту основные правки сводились к тому что бы отключить asserts которые проверяли что мы НЕ в андроиде после того как они попали в сборку из-за enable_extensions флага который я выставил. Второй вид ошибок был нерабочий код после #if BUILDFLAG(ENABLE_EXTENSIONS) который в итоге оказывался в незнакомой ему среде и зачастую не знал как компиляться (а потом линковка и не факт что он нашел бы все артефакты на которые рассчитывал).
Интересно, подумал я, когда это закончится? И сразу же сообразил что этим вопросом стоило озадачиться заранее. Сходив на chromium code search я запустил поиск по ENABLE_EXTENSIONS, получил список с более чем 2000 вхождений. IS_ANDROID дал мне еще более 6000 вхождений. Ну и плюсом к этому зачастую проверки в конфигах идут не assert(!is_android) а assert(is_win || is_mac || ...) и количество таких проверок уже так просто не выгуглишь, но при этом они есть. Настало время менять стратегию, подумал было я но тут же сам себя перебил со словами что стратегии то у нас и не было, так что менять тут нечего.
Второй подход был более удачным. Я решил выставить флаг enable_desktop_android_extensions и попробовать собрать с ним (target у нас все также android). Сейчас уже не помню точно, были ли вообще ошибки с этой сборкой, если и были — то немного, через пару-тройку дней у меня уже была на руках сборка. Но тут самое интересное — сборка была абсолютно нерабочая и бесполезная. То есть да, какой-то код имеющий отношение к расширениям подтянулся и собрался (по сути вся система webextensions, правда ОЧЕНЬ сильно урезанная). Но дальше то что? Ссылки вида chrome-extension не распознавались, chrome://extensions/ не открывался.
Ну так вот же! В очередной раз похвалив себя за сообразительность я понял что делать дальше. Надо для начала научить его открывать chrome://extensions/ а там будет видно чего не хватает. И вот это уже была на самом деле (без сарказма) стоящая идея. Оно сразу показало что нам не хватает webui (а это много, это что-то вроде привилегированного html+js на котором построена основная часть интерфейса chromium), и нам не хватает management и developer_private api — это webextensions api для внутренних нужд, они умеют получать список установленных расширений, включать/выключать их и в общем-то все то что вы видите в браузере на страничке chrome://extensions/ это дело рук этих api.
И вот тут уже дело пошло. Был четко очерченный периметр, было совершенно понятно что именно должно заработать и так же понятно как это проверять. Времени отняло это немного больше, насколько я помню первая рабочая сборка у меня появилась к концу второй недели с момента как я начал (то есть включая неудачные эксперименты). Что бы проверить работоспособность я добавил код из своего предыдущего коммита, который позволяет ставить расширения с любого сайта, не только официальные сторы, вот этот коммит, все 9 строк, собрал пустую болванку расширения которое только выводило в консоль сообщения с backgroung скрипта, content script-а и с popup-а. Выложил заготовку у себя в бложике и пошел ставить. Естественно оно сразу не заработало потому что crx installer в сборку включен не был, это отняло еще день-два, но в итоге случился happy end — расширенька качалась и ставилась. Правда в silent режиме, никаких уведомлений пользователю не выводилось и повлиять на процесс он не мог. А, да, на этом же этапе я уже правил андроидную реализацию подсистемы расширений от гуглеров потому что там многое было просто замокано, отдавались nullptr вместо различных сервисов и все такое.
Почему я это все рассказываю? Где code porn? Что тут вобще происходит? Так вот, то что я описываю — это тоже реальное программирование. Писать функции/классы/методы с нуля, применять паттерны, спорить о том где ставить запятую — в конце строки или в начале следующей, ну и само собой число пробелов в табе — это неотъемлимая часть программирования, да. Но этого не достаточно для того что бы лезть в проект с 32 миллионами строк кода. В проектах такого уровня на первое место выходит уже умение в проекте ориентироваться, это помогает понять где мы находимся и что мы тут можем сделать а чего нет. На втором месте (практически разделяя первое) — умение думать за того парня, абсолютно точно так же как на дороге. То есть не думать а как бы сделал я, нет. Думать а как бы сделал он. А что ему потом ревьюверы скажут. А как owner-ы кода отреагируют если их код затронут. И много много подобных мыслей. Так вот, воюя с ветряными мельницами такого масштаба начинаешь понимать предел своих возможностей и тут уже зачастую вопрос стоит не сделать наилучшим способом, нет. Сначала хотя бы просто сделать, да, совершенно верно, черновик, MVP, просто убедиться в том что это возможно. Потом уже на работающем (криво, косо и постоянно падающем) коде начинаются правки что бы этот код стабилизировать и уже только после этого можно каких то красивостей насыпать если вот прям совсем неймется. Но до этого надо еще дойти и вот эти мои рассуждения — это просто пример того как в отдельно взятом случае отдельно взятый коротышка таки дошел. Мало ли, вдруг кому-то пригодится.
Итак. Есть сборка которая умеет ставить расширения, отображать их в списке, отключать и включать обратно. Пережив щенячий восторг (да, поздно вечером я пару раз кричал в голос что я это сделал, спасибо соседям что не вызвали никого) начинаем думать что дальше.
Дальше были ресурсы, различные css и js файлы которые включаются в сборку и получаются по урлам типа chrome://theme и тому подобному. Это было не так сложно, я просто находил имя артефакта в сборке и пробегался по всем файлам где он упоминается, находил то место где стояло что то вроде if BUILDFLAGS(ENABLE_EXTENSIONS) или подобное и менял его так что бы он в сборку все таки попал.
Отдельным заходом было добавить модалок на установку и удаление расширения. Раз мы разрешаем ставить расширения с любого источника то пользователь обязательно должен иметь возможность понять что происходит, иначе он просто понаставит всякой дряни даже не понимая этого. Тут интересно — дело в том что под андроид немного свой ui и управляется он из мира java, а все действо с расширениями у нас происходит в с++. Поразмыслив я решил что надо просто подсмотреть как в коде уже что нибудь похожее сделано. Кандидатом выбрал модалку со скачиваниями, есть такая когда вы качаете что то опасное и хромиум пытается вас предупредить об этом. По функционалу оно прям идеально подходило, на расширениях нужна была такая же, только текст и реакция другая. Покурив один вечер сорцы и раскурив магию jni я это тоже впилил где то за пару дней. Основной задачей там было найти место в c++ коде из которого будет легче всего совершить переход в мир java, не слишком поздно что бы не пробрасывать в java слишком много потрохов из c++ и не слишком рано что бы не дублировать структуры в java.
Дальше надо было перенести поддержку tabs api, она мне нужна в Помогаторе. С ней вышло немножко тяжелее. Опять же андроид, то есть просто включить работающий код в сборку под андроид не получится, нужно писать свой. Вся работа с табами под андроид отличается от работы под десктопы, каких то абстракций под андроидом вообще нет, например tab_strip (вот эта полосочка табов наверху, где отрисовываются иконки и тайтл страницы) под андроидом отсутствует вообще (что до сих пор мне кажется странным, но возможно у этого решения были аргументы которых я просто еще не вижу) В общем худо бедно поддержку (частичную) я впилил, можно создавать, удалять таб и делать выборку. Что именно не работает будет ниже.
Далее поддержка popup-ов, это вот это окошко которое открывается когда мы кликаем на иконку расширения. Логика ui работающая на десктопах не подойдет для мобил, слишком мало места. К счастью любой попап можно открыть в отдельном табе если вбить ссылку вида chrome-extension://[extension_id]/[popup file name from manifest]. Для этого я немного подправил код генерирующий extensionInfo и добавил туда поле popupUrl, а при отображении расширения в chrome://extensions иконку расширения сделал кликабельной, так что тап по ней ведет к открытию попапа в отдельном табе. Вполне себе решение для мобил. (там еще предстоит научить добывать popup из второго манифеста и мониторить действия chrome.browserAction.setPopup, но для начала уже неплохо)
Собственно все на этом с моими похождениями, давайте посмотрим что получилось.
Результат
А получилось завести расширения браузера на андроиде. Работает:
- установка расширений с crx (пока только manifest v3)
- отображение установленных расширений в chrome://extensions
- включение/отключение/удаление расширений в списке
- включение/отключение developer mode
- открытие popup-а расширения
- перенесены некоторые api (часть из них только частично)
alarms.clearAll
alarms.create
alarms.get
alarms.getAll
automationInternal.disableDesktop
automationInternal.enableDesktop
automationInternal.enableTree
automationInternal.performAction
browsingData.remove
browsingData.removeAppcache
browsingData.removeCache
browsingData.removeCacheStorage
browsingData.removeCookies
browsingData.removeDownloads
browsingData.removeFileSystems
browsingData.removeFormData
browsingData.removeHistory
browsingData.removeIndexedDB
browsingData.removeLocalStorage
browsingData.removePasswords
browsingData.removePluginData
browsingData.removeServiceWorkers
browsingData.removeWebSQL
browsingData.settings
cookies.get
cookies.getAll
cookies.getAllCookieStores
cookies.getPartitionKey
cookies.remove
cookies.set
developerPrivate.addHostPermission
developerPrivate.addUserSpecifiedSites
developerPrivate.autoUpdate
developerPrivate.choosePath
developerPrivate.deleteExtensionErrors
developerPrivate.dismissMv2DeprecationNoticeForExtension
developerPrivate.dismissSafetyHubExtensionsMenuNotification
developerPrivate.getExtensionInfo
developerPrivate.getExtensionSize
developerPrivate.getExtensionsInfo
developerPrivate.getMatchingExtensionsForSite
developerPrivate.getProfileConfiguration
developerPrivate.getUserAndExtensionSitesByEtld
developerPrivate.getUserSiteSettings
developerPrivate.installDroppedFile
developerPrivate.isProfileManaged
developerPrivate.loadDirectory
developerPrivate.loadUnpacked
developerPrivate.notifyDragInstallInProgress
developerPrivate.openDevTools
developerPrivate.packDirectory
developerPrivate.reload
developerPrivate.removeHostPermission
developerPrivate.removeMultipleExtensions
developerPrivate.removeUserSpecifiedSites
developerPrivate.repairExtension
developerPrivate.requestFileSource
developerPrivate.setShortcutHandlingSuspended
developerPrivate.showOptions
developerPrivate.showPath
developerPrivate.updateExtensionCommand
developerPrivate.updateExtensionConfiguration
developerPrivate.updateProfileConfiguration
developerPrivate.updateSiteAccess
developerPrivate.uploadExtensionToAccount
downloads.acceptDanger
downloads.cancel
downloads.download
downloads.erase
downloads.getFileIcon
downloads.open
downloads.pause
downloads.removeFile
downloads.resume
downloads.search
downloads.setShelfEnabled
downloads.setUiOptions
downloads.show
downloads.showDefaultFolder
extension.isAllowedFileSchemeAccess
extension.isAllowedIncognitoAccess
extension.setUpdateUrlData
guestViewInternal.createGuest
guestViewInternal.destroyUnattachedGuest
guestViewInternal.setSize
i18n.getAcceptLanguages
idle.getAutoLockDelay
idle.queryState
idle.setDetectionInterval
management.createAppShortcut
management.generateAppForLink
management.get
management.getAll
management.getPermissionWarningsById
management.getPermissionWarningsByManifest
management.getSelf
management.installReplacementWebApp
management.launchApp
management.setEnabled
management.setLaunchType
management.uninstall
management.uninstallSelf
metricsPrivate.getFieldTrial
metricsPrivate.getHistogram
metricsPrivate.getIsCrashReportingEnabled
metricsPrivate.getVariationParams
metricsPrivate.recordBoolean
metricsPrivate.recordCount
metricsPrivate.recordEnumerationValue
metricsPrivate.recordLongTime
metricsPrivate.recordMediumCount
metricsPrivate.recordMediumTime
metricsPrivate.recordPercentage
metricsPrivate.recordSmallCount
metricsPrivate.recordSparseValue
metricsPrivate.recordSparseValueWithHashMetricName
metricsPrivate.recordSparseValueWithPersistentHash
metricsPrivate.recordTime
metricsPrivate.recordUserAction
metricsPrivate.recordValue
notifications.clear
notifications.create
notifications.getAll
notifications.getPermissionLevel
notifications.update
offscreen.closeDocument
offscreen.createDocument
offscreen.hasDocument
permissions.addHostAccessRequest
permissions.contains
permissions.getAll
permissions.remove
permissions.removeHostAccessRequest
permissions.request
power.releaseKeepAwake
power.requestKeepAwake
runtime.getBackgroundPage
runtime.getContexts
runtime.getPackageDirectoryEntry
runtime.getPlatformInfo
runtime.openOptionsPage
runtime.reload
runtime.requestUpdateCheck
runtime.restart
runtime.restartAfterDelay
runtime.setUninstallURL
storage.clear
storage.get
storage.getBytesInUse
storage.getKeys
storage.remove
storage.set
storage.setAccessLevel
system.cpu.getInfo
system.memory.getInfo
tabs.create
tabs.query
tabs.remove
test.getConfig
test.log
test.notifyFail
test.notifyPass
test.openFileUrl
test.sendMessage
test.sendScriptResult
test.waitForRoundTrip
webRequest.handlerBehaviorChanged
webRequestInternal.addEventListener
webRequestInternal.eventHandled
Работоспособность их еще надо проверять, например все что касается web apps совершенно точно работать не будет (и по хорошему их надо бы исключить), в downloads то что касается ui точно не работает, остальное надо проверить и так далее. Те api что имеют internal в названии — расширениям недоступны и используются в chromium-е для своих нужд.
Но для того что бы начать щупать и использовать расширения которые не требуют особых permissions — вполне достаточно.
Список функций неполный, например runtime.sendMessage в него не попал а он есть. Связано это с тем что я брал список из registry структуры подсистемы расширений, а часть функций навешивается в хуках и существует только в js мире, из с++ их просто не видно. Подробнее об этом можно посмотреть тут, я это использовал например при замене webRequests api, это позволило делать асинхронный перехват запроса и возвращать ответом результат другого запроса в сеть. Лучшим способом проверить существование и работоспособность функций будет постучаться к ним из консоли в браузере, ниже я распишу как это сделать.
Что не работает:
- установка расширений с manifest v2
- установка распакованных расширений
- запаковка и подпись расширений (это не особо на мобилах и нужно но если не сильно много времени отнимет — подключу)
- бОльшая часть api (переношу потихоньку, намерен как минимум сравняться в Microsoft Edge)
Как с этим начать играть?
Apk пока не выкладываю, придется пока собирать самим.
Сборка
Как и любая другая ветка Ultimatum-а, сборка не сильно отличается от сборки chromium-а. Во первых собираем на Linux, сборки под Андроид на других платформах не существует. Я собирал на Ubuntu, поэтому расскажу про нее, если у вас другой клон — загляните сюда.
После того как вы сделали fetch --nohooks chromium
у вас в src лежит репа chromium-а. Вот на этом этапе нужно сходить в src и сделать git checkout [номер версии]
То есть если вы хотите собрать Ultimatum 134.0.6964.1_android то сначала нужно сделать git checkout 134.0.6964.1
.
После этого идите по инструкции хромиума и соберите его. Я рекомендую этот шаг для того что бы убедиться что голый хромиум нормально собирается. Сама по себе документация хромиума вполне содержательная, по описываемым мною телодвижениям у меня была статья, можно заглянуть туда тоже.
для сборки задайте вот такую конфигурацию args.gn:
# Set build arguments here. See `gn help buildargs`.
target_os = "android"
target_cpu = "arm64"
is_debug = true
is_component_build = false
# symbol_level = 0
# blink_symbol_level = 0
# v8_symbol_level = 0
ffmpeg_branding = "Chrome"
proprietary_codecs = true
# enable_desktop_android_extensions = true
# enable_guest_view = true
enable_platform_apps = false
После того как хромиум собрался делаем:
git remote add gonzazoid https://github.com/gonzazoid/chromium.git
git fetch gonzazoid
git checkout 134.0.6964.1_android // нужная версия Ultimatum (посмотреть можно через git branch -a)
Делать gclient sync после этого не нужно, там уже все сделано, просто повторно запускаем сборку. Но! перед сборкой раскомментируйте в args.gn enable_desktop_android_extensions и enable_guest_view — именно они и включают всю магию.
Собственно все, после этого вы получите в out/Default (или как вы назвали директорию сборки) нужные артефакты. У меня директория сборки называется out/Android, так что в дальнейшем все пути будут с ней.
Подключите мобилу с десктопу, сделайте out/Android/bin/chrome_public_apk install
(подробнее можно посмотреть тут)
После этого сходите на https://gonzazoid.com и скачайте тестовое расширение. После установки откройте меню браузера и выберите extensions — в открывшемся табе вы должны увидеть привычный chrome://extensions и гордое установленное расширение.
Congrats!!! А теперь давайте его немного потискаем.
Remote debug
Для начала я рекомендую покурить вот эту статью, там совсем немножко. После этого НА ДЕСКТОПЕ в хроме открываем chrome://inspect#devices и находим наш хромиум запущенный на подключенном к десктопу android-устройстве.
Что мы тут можем себе позволить?
Для начала давайте сходим в гости к content script. Для этого выберите любой таб который был открыт после установки расширения (или обновите его что бы в него inject-нулся content script) и перейдите по ссылке inspect (если браузер на десктопе имеет ту же или более новую версию чем браузер на мобиле) или же inspect fallback (если браузер на десктопее постарее браузера на мобиле)
После этого в новом открывшемся окне dev-tools выберите вкладку console и в ней в выпадашке найдите наше расширение.
Собственно все, теперь все что вы набираете в консоли будет исполняться в окружении content script-а:
Ок. Давайте теперь навестим popup. Для этого НА МОБИЛЕ выберите в меню браузера extensions, в открывшемся списке расширений тапните по иконке расширения. Должен открыться таб (он будет пустой, не пугайтесь). На ДЕСКТОПЕ в chrome://inspect#devices найдите этот таб и нажмите inspect (inspect fallback). Открывшийся таб с devtools будет работать в контексте popup-а:
И у нас еще остался background script. Поскольку это manifest v3 то мы имеем дело с service worker-ом. Чтобы достучаться до него откройте любой таб на мобиле, зайдите в inspect с десктопа и во вкладке Application нажмите на See all registrations:
После этого у вас откроется таб:
С мобилы найдите нужный service worker и нажмите run.
В браузере на десктопе появится новая строка с service worker-ом:
жмите на его inspect и попадете в devtools в контексте service worker-a.
Собственно этого достаточно что бы пощупать расширеньку а при желании и протестить реальное расширение на работоспособность под андроидом. Не забывайте — это черновик, remote debug довольно нестабильный (особенно на service worker-e) и периодически отваливается.
Планы на будущее
Ну во первых сделать все хорошо.
Во вторых подтянуть второй манифест (не думаю что больше недели займет).
В третьих — перенести все те api что переносятся (тут непонятно но через месяц отпишусь — будет ясно с какими темпами иду).
А когда уже совсем все хорошо станет — причесать код и сделать его совместимым с другими сборками (сейчас с этой ветки можно собрать только андроид, другие сборки поломаны из-за внесенных правок и закомментированного кода)
Во всем остальном — посмотрим на реакцию пользователей, какие запросы появятся.
Но! Тут наступает щепетильный момент. Мне, для того что бы не упасть в голодный обморок нужно в ближайшие пару месяцев немного подзаработать, и самое быстрое решение — это перенести Помогатор под мобилы. Но если я туда пойду — я пойду не на пару месяцев, там работы много (не имеющей отношения к браузеру и расширениям) так что это поход месяца на три-четыре и там особо по api писать ничего не надо — все что нужно уже есть. Если я пойду устраиваться на работу к корпоратам (очень сильно не хочу) — это радикально снизит темпы по расширениям. Так что тут я в том числе рассчитываю на поддержку сообщества. Если то что я делаю — интересно и востребовано — поддержите меня, результаты моей работы вы видите, код я всегда выкладываю в открытый доступ. В моем бложике есть данные для донатов, тут я сильно расписывать не буду.
Собственно все, новостей на сегодня больше нет.
С вами был Тимур, всем хорошего настроения!
Комментарии (3)
Anselm_nn
15.02.2025 10:32разработчики Brave сохраняют манифест v2, можно ли такое решение адоптировать под их мобильную версию?
SaemonZixel
Смело, смело. Подпиливать такой огромный проект - Вы наверное весьма не ординарный программист!)
gonzazoid Автор
Да просто настоящий буйный :)