Оранжевый - новый синий: Как портировать chrome extension в...
TRANSCRIPT
![Page 1: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/1.jpg)
Оранжевый - новый синий
Как портировать Chrome Extension в Firefox Extension
или написать кросс-браузерное расширение с нуля
Борис Мосуновfacebook.com/mossounov
linkedin.com/in/borismossounovanotherguru.me
![Page 2: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/2.jpg)
Основные отличия архитектуры:Chrome Extension v.s. Firefox Addon
![Page 3: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/3.jpg)
Документация для разработчика
https://developer.chrome.com/extensionshttps://developer.mozilla.org/en-US/Add-ons
![Page 4: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/4.jpg)
Изучая документацию Mozilla можно наткнуться на:Legacy Extensions Overlay extensions
Restartless Extensions Bootstrapped ExtensionsAdd-on SDK Extensions
Jetpack SDK Add-on SDK
==
=
![Page 5: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/5.jpg)
Краткая история Firefox Addon:
Legacy Extensions / Overlay Extensions (XUL, JSM, XPCOM)
2004.06 - Firefox 0.9
![Page 6: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/6.jpg)
Краткая история Firefox Addon:
Legacy Extensions / Overlay Extensions (XUL, JSM, XPCOM)
2004.06 - Firefox 0.9
![Page 7: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/7.jpg)
Краткая история Firefox Addon:
Legacy Extensions / Overlay Extensions (XUL, JSM, XPCOM)
2004.06 - Firefox 0.9
browser.xul:
overlay.xul:
![Page 8: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/8.jpg)
Краткая история Firefox Addon:
Legacy Extensions / Overlay Extensions (XUL, JSM, XPCOM)
2004.06 - Firefox 0.9
Mozilla starts Jetpack SDK project2009.09
![Page 9: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/9.jpg)
![Page 10: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/10.jpg)
Краткая история Firefox Addon:
Legacy Extensions / Overlay Extensions (XUL, JSM, XPCOM)
2004.06 - Firefox 0.9
Mozilla starts Jetpack SDK project2009.09
Restartless Extensions / Bootstrapped Extensions2011.03 - Firefox 4+
Jetpack SDK 0.1 releaseAddon SDK extensionscfx tool (python powered)
2011.06
jpm tool (node.js powered)2015.05 - Firefox 38
![Page 11: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/11.jpg)
Краткая история Chrome Extension:
Chrome стал поддерживать расширения2009.09
Запущен Google Chrome Store с блэкджеком и расширениями
2010.12
![Page 12: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/12.jpg)
Краткая история:2004.06 - Firefox 0.9
2009.09
2011.03 - Firefox 4+
2011.06
2015.05 - Firefox 38
2010.12
Chrome стал поддерживать расширения
Legacy Extensions / Overlay Extensions (XUL, JSM, XPCOM)addons.mozilla.org
Mozilla starts Jetpack SDK project
Restartless Extensions / Bootstrapped Extensions
Jetpack SDK 0.1 releaseAddon SDK extensionscfx tool (python powered)
jpm tool (node.js powered)
Запущен Google Chrome Store с блэкджеком и расширениями
![Page 13: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/13.jpg)
Изучая документацию Mozilla нужно читать про:Legacy Extensions Overlay extensions
Restartless Extensions Bootstrapped ExtensionsAdd-on SDK Extensions
Jetpack SDK Add-on SDK
==
=
![Page 14: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/14.jpg)
Из чего состоит среднестатистическое браузерное расширение?
manifest
background script
content scripts, styles, assets
toolbar button popup
locales
embedded pages(options / help)
![Page 15: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/15.jpg)
Основное и самое главное отличие Chrome Extension и Firefox Addon
состоит в способах взаимодействия javascript сценариев из 3 блоков:
background.js
contentscript.js popup.js
![Page 16: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/16.jpg)
В Google Chrome целых два API обмена сообщениями:
• Simple one-time requests• Long-lived connections
https://developer.chrome.com/extensions/messaging
![Page 17: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/17.jpg)
Simple one-time requests
![Page 18: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/18.jpg)
chrome.runtime.sendMessage({greeting: «hello»}, function(response) {
console.log(response.farewell);});
background.js
contentscript.js popup.js
![Page 19: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/19.jpg)
chrome.tabs.sendMessage(tabs[0].id, {greeting: «hello»}, function(response) {console.log(response.farewell);});
background.js
contentscript.js
![Page 20: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/20.jpg)
chrome.runtime.onMessage.addListener( function(request, sender, callback) { console.log(sender.tab ? "from a content script:" +
sender.tab.url : "from the extension"); if (request.greeting ==
"hello") callback({farewell:
"goodbye"}); });
background.js
contentscript.js popup.js
?
![Page 21: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/21.jpg)
Long-lived connections
![Page 22: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/22.jpg)
port.postMessage({joke: "Knock knock»});
port.onMessage.addListener(function(msg) { if (msg.question == "Who's there?"){ port.postMessage({answer: "#4front"}); }});
background.js
contentscript.js popup.js
![Page 23: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/23.jpg)
Window chrome.extension.getBackgroundPage()
background.js
contentscript.js popup.js
contentscript.js и popup.js могут:
Нужна кросс-браузерность?
Если да, то не стоит.Если нет, то можно.
![Page 24: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/24.jpg)
background.js
contentscript.js popup.js
• есть два разных API обмена сообщениями
• contentscript.js и popup.js могут получить background window и сохранить там объект с методами
• все могут обмениваться безымянными сообщениями, задавая колбек для ответа.
• background.js ничего не знает об остальных, пока они к нему не подключатся
![Page 25: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/25.jpg)
А что там у Firefox?
https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Content_Scripts/using_port
![Page 26: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/26.jpg)
port.emit("myMessage", msg);
port.on("myMessage",function(msg) { if (msg.question == "Who's there?") port.emit("myMessage.reply", {
answer: "#4front"});});
background.js
contentscript.js popup.js
![Page 27: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/27.jpg)
![Page 28: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/28.jpg)
background.js
contentscript.js popup.js
• все могут обмениваться именованными сообщениями без колбэков
• background.js создает contentscript.js и popup.js и может ими управлять.
![Page 29: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/29.jpg)
callbacks = {«1234»:
function(payload){},«1235»:
function(payload){}
}
message = {action:
«myMessage.reply»,uid: 1234,paylaod: {}}
message = {}
message = {action:
«myMessage»,paylaod: {}}
message = {action:
«myMessage»,uid: 1234,paylaod: {}}
![Page 30: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/30.jpg)
Разберем отличия Mozilla Extension и Chrome Extensionпо составляющим
manifest
background script
content scripts, styles, assets
toolbar button popup
locales
embedded pages(options / help)
![Page 31: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/31.jpg)
manifest
manifest.jsonописывает:• название,
описание, версия, лицензия,
• расположение всех компонентов,
• права доступа расширения
package.jsonописывает:• название, описание,
версия, лицензия,• расположение
background script
а background scriptподгружает все компоненты
![Page 32: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/32.jpg)
manifest
Как сделать кросс-браузерно:
Настроить grunt task, который будет синхронизировать общие поля в manifest.json и package.json (версию, название, описание…)
![Page 33: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/33.jpg)
• Виртуальная html страница со своим window js-объектом,
• этот window выполняется в одном контексте content script и popup script, примерно как iframe
• Отдельный JavaScript, без window.
• выполняется в отдельном от остальных контексте
• Из него подгружаются content scripts, styles, popup, etc.
• EcmaScript 6
background script
![Page 34: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/34.jpg)
Как сделать кросс-браузерно:
1. Вообще не пользоваться chrome.extension.getBackgroundPage()
2. Для firefox - отдельный background script поскольку все firefox addon sdk на ES6.
3. Написать прослойку - систему обмена с content-script и popup script именованными сообщениями c колбеками который использовать и там и там
4. Заранее разделять скрипты для background и content scripts, вплоть до клиент-серверной архитектуры.
background script
![Page 35: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/35.jpg)
• выполняются в изолированном мире веб-страницы
• можно получить доступ к background window
• кросс-доменные запросы в рамках прав доступа
• выполняются в изолированном мире веб-страницы
• выполняются в контексте изолированном от background script
• кросс-доменные запросы запрещены
content scripts, styles, assets
![Page 36: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/36.jpg)
Как сделать кросс-браузерно:
1. Вообще не пользоваться chrome.extension.getBackgroundPage()
2. Написать прослойку - систему обмена с content-script и popup script именованными сообщениями c колбеками который использовать и там и там
3. Заранее разделять скрипты для background и content scripts, вплоть до клиент-серверной архитектуры.
4. Для ajax запросов в firefox использовать Request на стороне background и вышеупомянутую систему обмена сообщениями с колбеками
content scripts, styles, assets
![Page 37: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/37.jpg)
• кнопка может быть только одна
• можно получить доступ к background window
• попап загружается по клику на кнопку и выгружается после закрытия
• javascript подгружается через тег <script>
• размер попапа вычисляется автоматически
• кнопок может быть несколько
• контекст изолирован от background script
• попап загружается при старте экстеншена и остается в памяти
• скрипты надо подгружать как контент скрипты
• размер попапа задается вручную
toolbar button popup
![Page 38: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/38.jpg)
Как сделать кросс-браузерно:
1. Вообще не пользоваться chrome.extension.getBackgroundPage()2. Считать, что кнопка на тулбаре может быть только одна.3. Писать popup script так, словно он загружается один раз и
выгружается при закрытии браузера (отключении экстеншена), при этом popup должен реагировать на события экстеншена и, а не рендериться один раз.
4. При этом помнить, что все же в разных браузерах он загружается в разное время.
5. В firefox скрипты подгружать через content scripts, а <script> блокировать (https://developer.mozilla.org/en-US/Add-ons/SDK/Tutorials/Display_a_Popup).
toolbar button popup
![Page 39: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/39.jpg)
Формат: .json
доступны из:• background script• content script• popup script
Формат: .properties
доступны из:• только background
script, который может передать остальным
• в popup.html можно использовать идентификаторы
locales
![Page 40: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/40.jpg)
Как сделать кросс-браузерно:
1. Выбрать один из форматов как основной (json).2. Использовать в firefox файлы .properties только для хранения идентификатора языка.3. В Firefox Запрашивать из content script и popup script весь словарь сразу, который background считывает из json. Или передавать словарь при инициализации контент и попап скриптов в конфиге.
locales
![Page 41: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/41.jpg)
Как собрать экстеншен
npm install -g yonpm install -g generator-chrome-extensionnpm install -g generator-firefox-extensionnpm install -g jpm
![Page 42: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/42.jpg)
Как собрать?yo chrome-extension yo firefox-extension
Основное отличие:background scripts - в /libвсе остальное - в /data
![Page 43: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/43.jpg)
Cделаем единообразноyo chrome-extension yo firefox-extension
![Page 44: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/44.jpg)
Сводим воединоyo chrome-extension yo firefox-extension
.gitignore
.gitignore
1. Объединяем в проект
app-chromeapp-firefox
2. grunt copy:app-chrome/scripts/bg-* ->
app-firefox/libвсе остальное ->
app-firefox/data
3. добавляем в .gitignore
app-firefox/libapp-firefox/data
![Page 45: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/45.jpg)
Сводим воединоyo chrome-extension yo firefox-extension
или…
просто все сводим все к структуре папок firefox и обновляем manifest.json.
![Page 46: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/46.jpg)
en-US.properties содержит одну строку:
lng= en
это служит только для определения локали,а национализированные строки подгружаются из .json
в bg-main-firefox
locales
![Page 47: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/47.jpg)
и последнее…
generator-firefox-extension использует для сборки утилиту cfx, запуская ее через shell
утилита cfx устарела и ее надо заменить на утилиту jpm
но это не сложно
![Page 48: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/48.jpg)
cfx syntax
![Page 49: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/49.jpg)
jpm syntax
не забыть добавить:cd app-firefox
![Page 50: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/50.jpg)
Как сделать кросс-браузерно:• Для генерации пустого проекта использовать yeoman генераторы: generator-chrome-
extension и generator-firefox-extension. Заменить cfx на jpm.• Свести всю базу кода в один проект и настроить grunt build.• Настроить grunt task, который будет синхронизировать общие поля в manifest.json и
package.json (версию, название, описание…).• Сделать прослойку адаптер, которая будет использовать api того браузера, в котором
запущено расширение.• Реализовать кросс-браузерный механизм обмена именованных сообщений с колбеками.• Вообще не пользоваться chrome.extension.getBackgroundPage()• Сделать два отдельных background script для Chrome и Firefox.• Заранее разделять скрипты для background и content scripts, вплоть до клиент-серверной
архитектуры.• Для ajax запросов в firefox использовать Request на стороне background и
вышеупомянутую систему обмена сообщениями с колбеками, для Chrome - jQuery.ajax отлично подойдет.
• Считать, что кнопка на тулбаре может быть только одна.• Писать popup script так, словно он загружается один раз и выгружается при закрытии
браузера (отключении экстеншена). Другими словами popup должен подписываться на события экстеншена и реагировать на них, а не рендериться один раз. При этом помнить, что все же в разных браузерах он загружается в разное время.
• В firefox popup скрипты подгружать через content scripts, а <script> блокировать.• Локали хранить в формате Chrome - json. В firefox использовать файлы .properties только
для хранения идентификатора языка и запрашивать из content script и popup script весь словарь сразу, который background считывает из json.
manifestbackground scriptcontent scripts, styles, assets
toolbar button popupвсе вместе
![Page 51: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/51.jpg)
А стоит ли оно того?
![Page 52: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/52.jpg)
Использование браузеров в РФ
![Page 53: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/53.jpg)
Использование браузеров в МИРЕ
![Page 54: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/54.jpg)
Чтобы реализовать экстеншен, подобный по функционалу на Chrome Extension или Firefox
Extension, необходимо писать на C#, так что про кросс-браузерность здесь можно забыть.
Google Chrome = Webkit = Opera = Yandex Browser, и т.д.
Apple Safari - это почти Webkit.
К слову сказать:
![Page 55: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/55.jpg)
А что там у Safari?
![Page 56: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/56.jpg)
Таким образом, если следовать описанным здесь рекомендациям, вы получите
расширение совместимое со всеми основными браузерами, не считая IE
![Page 57: Оранжевый - новый синий: Как портировать Chrome Extension в Firefox Extension](https://reader038.vdocuments.net/reader038/viewer/2022110108/587f32d91a28ab121d8b661d/html5/thumbnails/57.jpg)
Вопросы?