Ваш первый WebExtension
В этой статье мы пройдём весь путь создания WebExtension для Firefox, от начала и до конца. Это дополнение будет просто добавлять красную рамку ко всем страницам, загруженным с «mozilla.org» или любого из его поддоменов.
Для начала вам нужен Firefox 45 или более поздней версии.
Написание WebExtension
Создайте новую директорию (папку) и перейдите в неё:
manifest.json
Теперь создайте новый файл, назовите его «manifest.json» в папке «borderify». Вставьте туда следующий код:
В некоторых случаях вам нужно указать ID для вашего дополнения. Если вам нужно указать ID дополнения включите ключ applications в manifest.json и установите его свойство gecko.id :
icons/border-48.png
Дополнение должно иметь иконку (значок). Эта иконка будет показана в списке дополнений в Менеджере Дополнений. Наш файл manifest.json сообщает, что иконка будет находиться в файле «icons/border-48.png».
Вы можете использовать собственную иконку. Её размер должен быть 48×48 пикселей. Вы можете также использовать иконку размером 96×96 пикселей для отображения на мониторах высокого разрешения. В этом случае вам необходимо указать её в качестве свойства «96» объекта » icons» в файле manifest.json:
Также вы можете создать иконку в формате SVG и она будет корректно масштабироваться.
borderify.js
Наконец, создайте в директории «borderify» файл с именем «borderify.js» и поместите туда следующий код:
Этот скрипт будет встраиваться в страницу, которая совпадает с шаблоном, указанном в ключе content_scripts файла manifest.json. Этот скрипт имеет прямой доступ ко всему документу, как если бы он был загружен самой страницей.
Пробуем
Сначала внимательно проверьте, что вы правильно разместили файлы и дали им правильные имена:
Установка
Начиная с версии Firefox 45 вы можете временно установить WebExtension с локального диска.
Откройте страницу «about:debugging», кликните «Load Temporary Add-on» и выберите файл manifest.json:
Теперь ваше дополнение установлено и останется в браузере до его перезапуска.
Для проверки, зайдите на страницу «about:addons» чтобы открыть Менеджер Дополнений. Вы должны увидеть своё дополнение с именем и иконкой:
Также, вы можете запускать WebExtension из командной строки, используя web-ext.
Тестирование
Теперь зайдите на любую страницу домена «mozilla.org» и вы должны будете увидеть красную границу вокруг страницы:
Поэкспериментируйте немного. Поменяйте цвет границы или сделайте ещё что-нибудь с содержимым на странице. После того, как изменённый скрипт будет сохранён, а страница перезагружена, вы сразу увидите изменения:
Обратите внимание, что после изменения файла manifest.json, вы должны вручную перезагрузить своё дополнение. В настоящий момент это значит, что вам нужно перезагрузить Firefox, а затем снова загрузить своё дополнение на странице «about:debugging». Мы работаем над улучшением этого процесса.
Упаковка и публикация
Чтобы другие люди могли использовать ваше дополнение, вам необходимо запаковать его. Дополнения Firefox в запакованном виде являются XPI файлами, которые представляют собой обычные ZIP архивы с расширением «xpi».
При упаковке необходимо учитывать следующее: в ZIP архиве должны быть только файлы, а не содержащая их директория (директория «borderify» не должна попасть в архив). Для того, чтобы создать правильный XPI файл из вашего дополнения, в командной строке перейдите в директорию «borderify» и выполните следующую команду:
Начиная с Firefox 43 все дополнения должны быть подписаны прежде чем они будут установлены в браузер. Вы можете снять это ограничение только в Firefox Developer Edition или Firefox Nightly при помощи следующих шагов:
Что дальше?
Теперь, когда вы имеете представление о разработке дополнений для Firefox, вы можете:
Пишем безопасное браузерное расширение
В отличие от распространенной «клиент-серверной» архитектуры, для децентрализованных приложений характерно:
Существует 2 относительно безопасных хранилища для ключей пользователей — хардверные кошельки и браузерные расширения. Хардверные кошельки в большинстве своем максимально безопасны, однако сложны в использовании и далеко не бесплатны, а вот браузерные расширения являются идеальным сочетанием безопасности и простоты в использовании, а еще могут быть совершенно бесплатны для конечных пользователей.
Учитывая все это, мы захотели сделать максимально безопасное расширение, которое упрощает разработку децентрализованных приложений, предоставляя простой API для работы с транзакциями и подписями.
Об этом опыте мы вам и расскажем ниже.
В статье будет пошаговая инструкция как написать браузерное расширение, с примерами кода и скриншотами. Весь код вы можете найти в репозитории. Каждый коммит логически соответствует разделу данной статьи.
Краткая история браузерных расширений
Браузерные расширения существуют достаточно давно. В Internet Explorer они появились еще в 1999-м году, в Firefox — в 2004-м. Тем не менее, очень долго не было единого стандарта для расширений.
Можно сказать, что он появился вместе с расширениями в четвертой версии Google Chrome. Конечно, никакой спецификации тогда не было, но именно API Chrome стал ее основой: завоевав большую часть рынка браузеров и имея встроенный магазин приложений, Chrome фактически задал стандарт для браузерных расширений.
У Mozilla был свой стандарт, но, видя популярность расширений для Chrome, компания решила сделать совместимый API. В 2015 году по инициативе Mozilla в рамках World Wide Web Consortium (W3C) была создана специальная группа для работы над спецификациями кроссбраузерных расширений.
За основу был взят уже существующий API расширений для Сhrome. Работа велась при поддержке Microsoft (Google в разработке стандарта участвовать отказался), и в результате появился черновик спецификации.
Формально спецификацию поддерживают Edge, Firefox и Opera (заметьте, что в этом списке отсутствует Chrome). Но на самом деле стандарт во многом совместим и с Chrome, так как фактически написан на основе его расширений. Подробнее о WebExtensions API можно прочитать здесь.
Структура расширения
Единственный файл, который обязательно нужен для расширения — манифест (manifest.json). Он же является “точкой входа” в расширение.
Манифест
По спецификации файл манифеста является валидным JSON файлом. Полное описание ключей манифеста с информацией о том, какие ключи в поддерживается в каком браузере, можно посмотреть здесь.
Ключи, которых нет в спецификации, “могут” быть проигнорированы (и Chrome, и Firefox пишут об ошибках, но расширения продолжают работать).
А я бы хотел обратить внимание на некоторые моменты.
Контекст выполнения
У расширения есть три контекста исполнения кода, то есть, приложение состоит из трех частей с разным уровнем доступа к API браузера.
Extension context
Здесь доступна большая часть API. В этом контексте «живут»:
Этот контекст существует независимо от окон и вкладок браузера. Background page существует в единственном экземпляре и работает всегда (исключение — event page, когда background-скрипт запускается по событию и «умирает» после его выполнения). Popup page существует, когда открыто окно popup, а Custom page — пока открыта вкладка с ней. Доступа к другим вкладкам и их содержимому из этого контекста нет.
Content script context
Web page context
Это собственно сама веб-страница. К расширению она не имеет никакого отношения и доступа туда не имеет, кроме случаев, когда в манифесте явно не указан домен этой страницы (об этом — ниже).
Обмен сообщениями
Разные части приложения должны обмениваться сообщениями между собой. Для этого существует API runtime.sendMessage для отправки сообщения background и tabs.sendMessage для отправки сообщения странице (контент-скрипту, popup’у или веб странице при наличии externally_connectable ). Ниже приведен пример при обращении к API Chrome.
Сервер или background:
Схема приложения
Давайте сделаем браузерное расширение, которое хранит приватные ключи, предоставляет доступ к публичной информации (адрес, публичный ключ общается со страницей и позволяет сторонним приложениям запросить подпись транзакций.
Разработка приложения
Наше приложение должно как взаимодействовать с пользователем, так и предоставлять странице API для вызова методов (например, для подписи транзакций). Обойтись одним лишь contentscript не получится, так как у него есть доступ только к DOM, но не к JS страницы. Подключаться через runtime.connect мы не можем, потому что API нужен на всех доменах, а в манифесте можно указывать только конкретные. В итоге схема будет выглядеть так:
Начало
Весь код браузерного расширения доступен на GitHub. В процессе описания будут ссылки на коммиты.
Начнем с манифеста:
Создаем пустые background.js, popup.js, inpage.js и contentscript.js. Добавляем popup.html — и наше приложение уже можно загрузить в Google Chrome и убедиться, что оно работает.
Чтобы убедиться в этом, можно взять код отсюда. Кроме того, что мы сделали, по ссылке настроена сборка проекта с помощью webpack. Чтобы добавить приложение в браузер, в chrome://extensions нужно выбрать load unpacked и папку с соответствующим расширением — в нашем случае dist.
Теперь наше расширение установлено и работает. Запуститьинструменты для разработчиков для разных контекстов можно следующим образом:
Доступ к консоли контент-скрипта осуществляется через консоль самой страницы, на которой он запущен.
Обмен сообщениями
Итак, нам необходимо установить два канала связи: inpage background и popup background. Можно, конечно, просто отправлять сообщения в порт и изобрести свой протокол, но мне больше нравится подход, который я подсмотрел в проекте с открытым кодом metamask.
Это браузерное расширение для работы с сетью Ethereum. В нем разные части приложения общаются через RPC при помощи библиотеки dnode. Она позволяет достаточно быстро и удобно организовать обмен, если в качестве транспорта ей предоставить nodejs stream (имеется в виду объект, реализующий тот же интерфейс):
Теперь мы создадим класс приложения. Оно будет создавать объекты API для popup и веб-страницы, а также создавать dnode для них:
Здесь и далее вместо глобального объекта Chrome мы используем extentionApi, который обращается к Chrome в браузере от Google и к browser в других. Делается это для кроссбраузерности, но в рамках данной статьи можно было бы использовать и просто ‘chrome.runtime.connect’.
Создадим инстанс приложения в background скрипте:
Так как dnode работает со стримами, а мы получаем порт, то необходим класс-адаптер. Он сделан при помощи библиотеки readable-stream, которая реализует nodejs-стримы в браузере:
Теперь создаем подключение в UI:
Затем мы создаем подключение в content script:
Так как API нам нужен не в контент-скрипте, а непосредственно на странице, мы делаем две вещи:
Теперь создаем объект api в inpage и заводим его global:
У нас готов Remote Procedure Call (RPC) с отдельным API для страницы и UI. При подключении новой страницы к background мы можем это увидеть:
Пустой API и origin. На стороне страницы мы можем вызвать функцию hello вот так:
Работать с callback-функциями в современном JS — моветон, поэтому напишем небольшой хелпер для создания dnode, который позволяет передавать в объект API в utils.
Объекты API теперь будут выглядеть вот так:
Получение объекта от remote следующим образом:
А вызов функций возвращает промис:
Версия с асинхронными функциями доступна здесь.
В целом, подход с RPC и стримами кажется достаточно гибким: мы можем использовать steam multiplexing и создавать несколько разных API для разных задач. В принципе, dnode можно использовать где угодно, главное — обернуть транспорт в виде nodejs стрима.
Альтернативой является формат JSON, который реализует протокол JSON RPC 2. Однако он работает с конкретными транспортами (TCP и HTTP(S)), что в нашем случае не применимо.
Внутренний стейт и localStorage
Нам понадобится хранить внутренний стейт приложения — как минимум, ключи для подписи. Мы можем достаточно легко добавить стейт приложению и методы для его изменения в popup API:
В background обернем все в функцию и запишем объект приложения в window, чтобы можно было с ним работать из консоли:
Добавим из консоли UI несколько ключей и посмотрим, что получилось со стейтом:
Стейт нужно сделать персистентным, чтобы при перезапуске ключи не терялись.
Хранить будем в localStorage, перезаписывая при каждом изменении. Впоследствии доступ к нему также будет необходим для UI, и хочется также подписываться на изменения. Исходя из этого удобно будет сделать наблюдаемое хранилище (observable storage) и подписываться на его изменения.
Использовать будем библиотеку mobx (https://github.com/mobxjs/mobx). Выбор пал на нее, так как работать с ней не приходилось, а очень хотелось ее изучить.
Добавим инициализацию начального стейта и сделаем store observable:
«Под капотом» mobx заменил все поля store на proxy и перехватывает все обращения к ним. На эти обращения можно будет подписываться.
Далее я буду часто использовать термин “при изменении”, хотя это не совсем корректно. Mobx отслеживает именно доступ к полям. Используются геттеры и сеттеры прокси-объектов, которые создает библиотека.
Декораторы action служат двум целям:
В background добавим инициализацию и сохранение стейта в localStorage:
Интересна здесь функция reaction. У нее два аргумента:
В отличие от redux, где мы явно получаем стейт в качестве аргумента, mobx запоминает к каким именно observable мы обращаемся внутри селектора, и только при их изменении вызывает обработчик.
В консоли popup снова добавим несколько ключей. На этот раз они попали еще и в localStorage:
При перезагрузке background-страницы информация остается на месте.
Весь код приложения до этого момента можно посмотреть здесь.
Безопасное хранение приватных ключей
Хранить приватные ключи в открытом виде небезопасно: всегда есть вероятность того, что вас взломают, получат доступ к вашему компьютеру и так далее. Поэтому в localStorage мы будем хранить ключи в зашифрованном паролем виде.
Для большей безопасности добавим приложению стейт locked, в котором доступа к ключам не будет совсем. Мы будем автоматически переводить расширение в стейт locked по таймауту.
Mobx позволяет хранить только минимальный набор данных, а остальное автоматически рассчитывать на их основе. Это — так называемые computed properties. Их можно сравнить с view в базах данных:
Теперь мы храним только шифрованные ключи и пароль. Все остальное вычисляется. Перевод в стейт locked мы делаем с помощью удаления пароля из стейта. В публичном API появился метод для инициализации хранилища.
Код до этого шага находится здесь.
Транзакции
Итак, мы подошли к самому главному: созданию и подписи транзакций в блокчейне. Мы будем использовать блокчейн WAVES и библиотеку waves-transactions.
Для начала добавим в стейт массив сообщений, которые необходимо подписать, затем — методы добавления нового сообщения, подтверждения подписи и отказа:
Если не сделать observable вручную, то mobx сделает это сам при добавлении в массив messages. Однако он создаст новый объект, на который у нас не будет ссылки, а она понадобится для следующего шага.
Далее мы возвращаем промис, который резолвится при изменении статуса сообщения. За статусом следит reaction, который сам себя «убьет» при смене статуса.
Код методов approve и reject очень прост: мы просто меняем статус сообщения, предварительно подписав его, если необходимо.
Approve и reject мы выносим в API UI, newMessage — в API страницы:
Теперь попробуем подписать транзакцию расширением:
В целом все готово, осталось добавить простой UI.
Интерфейсу нужен доступ к стейту приложения. На стороне UI мы сделаем observable стейт и добавим в API функцию, которая будет этот стейт менять. Добавим observable в объект API, полученный от background:
В конце мы запускаем рендер интерфейса приложения. Это react-приложение. Background-объект просто передается при помощи props. Правильно, конечно, сделать отдельный сервис для методов и store для стейта, но в рамках данной статьи этого достаточно:
С помощью mobx очень просто запускать рендер при изменении данных. Мы просто вешаем декоратор observer из пакета mobx-react на компонент, и рендер будет автоматически вызываться при изменении любых observable, на которые ссылается компонент. Не нужно никаких mapStateToProps или connect, как в redux. Все работает сразу «из коробки»:
Остальные компоненты можно посмотреть в коде в папке UI.
При получении объекта remote создается reaction на изменение стейта, который вызывает функцию на стороне UI.
Последний штрих — добавим отображение новых сообщений на иконке расширения:
Итак, приложение готово. Веб-страницы могут запрашивать подпись транзакций:
Код доступен по этой ссылке.
Заключение
Если вы дочитали статью до конца, но у вас остались вопросы, вы можете задать их в репозитории с расширением. Там же вы найдете коммиты под каждый обозначенный шаг.
А если вам интересно посмотреть код настоящего расширения, то вы сможете найти это здесь.
Код, репозиторий и описание работы от siemarell
Создание расширения FireFox для начинающих
В данной статье представлена пошаговая инструкция по разработке простейшего расширения для FireFox.
Это частичный перевод оригинальной статьи.
Это не моя статья, а моего друга (его мыльце: templar8@gmail.com). Он очень хочет попасть на Хабр. У меня самого не хватает кармы для инвайта.
Вступление
Это руководство — пошаговая инструкция по созданию простейшего расширения. Мы попробуем добавить в строку состояния еще одну небольшую панельку с фразой «Hello, World!».
Подготовка окружения
Расширения упаковываются и распространяются в виде zip-файлов или пакетов с расширением XPI.
Вот пример типичной внутренней структуры XPI-файла:
exampleExt.xpi:
/install.rdf
/components/*
/components/cmdline.js
/defaults/
/defaults/preferences/*.js
/plugins/*
/chrome.manifest
/chrome/icons/default/*
/chrome/
/chrome/content/
Нам нужно создать структуру каталогов, похожую на эту. Для начала создайте корневую директорию расширения (например, C:\extensions\my_extension\ или
\
install.rdf
chrome.manifest
chrome\
content\
Дополнительная информация о настройке окружения находится по этой ссылке.
Сценарий установки
Откройте файл install.rdf и добавьте в него следующий текст:
— это «связующая точка» XUL-слоя.
XUL-слои
Пример документа XUL-слоя
Тег с id, равным « status-bar » указывает на виджет браузера в который мы хотим добавить наш элемент.
Тег — это новый виджет, который мы хотим добавить.
Chrome URIs
Chrome URI состоит из нескольких частей:
Когда вы загружаете что-либо, оперируя Chrome URI, Firefox использует реестр Chrome (Chrome Registry) для преобразования этого URI в реальный путь к файлу на диске (или в JAR архивах).
Создание Chrome Manifest
Для получения большей информации о Chrome Manifest и всех его свойствах можно обратиться к справочному руководству.
Откройте файл chrome.manifest, который был создан в корневом каталоге вашего расширения. Добавьте следующий код:
content sample chrome/content/
(Не забудьте о закрывающем слеше, «/»! Без него пакет не будет зарегистрирован.)
Обратите внимание, что файлы контента, локализаций и скинов должны находиться внутри каталога content, locale и skin подкаталога chrome соответственно.
Сохраните файл. Теперь, когда вы запустите Firefox с вашим расширением (как это сделать, будет описано ниже), пакет chrome будет зарегистрирован.
Регистрация слоя
Теперь необходимо связать ваш слой с окном браузера. Для этого добавьте следующие строки в файл chrome.manifest :
Тестирование
Во-первых, мы должны сообщить Firefox о нашем расширении. На стадии разработки для Firefox версии 2 и выше, вы можете указать откуда брать новое расширение, и браузер будет его загружать после каждого перезапуска.
Запустите Firefox. Firefox по текстовой ссылке сам найдет каталог с вашим расширением и установит его. После запуска браузера вы увидите надпись «Hello, World!» в правой части строки состояния.
Создание пакета
Теперь, когда расширение работает, вы можете создать пакет для последующего распространения и установки.
Как разработать расширение для Firefox
Признайте, что вы всегда хотели знать, как разработать расширение для Firefox, но у вас никогда не было времени на обучение Здесь я проведу вас до конца, и в конце статьи мы создадим полнофункциональное расширение для Firefox!
Наша цель
Мы создадим расширение Firefox, чтобы найти все ссылки на текущей веб-странице, выделить те, которые имеют target атрибут, и предупредить вас, сколько ссылок оно нашло. Хорошая часть заключается в том, что после того, как вы это сделаете, у вас есть понимание разработки расширений Firefox, а также проект для любого расширения, которое вы захотите разработать в будущем.
Что вам нужно — настройка среды разработки
Сначала давайте начнем с настройки среды разработки. Вам нужен Firefox (да) и в основном любой редактор кода, который вы предпочитаете. Затем, есть несколько рекомендуемых вещей для подготовки Firefox:
Создайте другой профиль разработки
Первым шагом является создание другого профиля в Firefox, так как вы сделаете некоторые настройки и изменения, которые вы, вероятно, не хотите использовать в своем обычном профиле. В моем случае я создал новый профиль разработки под названием «dev». Шаги, чтобы сделать это:
Диспетчер профилей в Windows
Менеджер профилей на Mac
Менеджер профилей в Linux
Настройки конфигурации для Firefox
Откройте Firefox с помощью диспетчера профилей (процесс, описанный выше, или установите профиль разработки по умолчанию во время разработки расширений). Затем введите about:config в адресной строке. Он предупредит вас об изменении настроек, но это нормально, так как вы будете делать только незначительные изменения для разработки. Вы можете отфильтровать существующие настройки, и если какие-либо из приведенных ниже настроек не существуют, вы можете просто создать их.
Рекомендуемые настройки
Это хорошо для включения ошибок расширения в Консоли ошибок Firefox (Инструменты> Консоль ошибок), отключения кэширования XUL и т. Д.
Дополнительные настройки
Это не обязательно, но они могут помочь вам. Лично я этим не пользуюсь.
Укажите ваш каталог расширений Firefox на ваше расширение
Вместо постоянной подготовки и переустановки вашего расширения, есть простой способ добавить указатель из каталога расширений Firefox в местоположение вашего кода. Для этого вы должны сначала найти каталог своего профиля:
Найдите каталог своего профиля
В каталоге профилей вы найдете все настройки для ваших профилей Firefox, включая информацию о расширениях.
Найти каталог профиля в Windows
Найти каталог профиля на Mac
Откройте Терминал и введите CD
Найти каталог профиля в Linux
Откройте терминал и введите CD
Указывая на расширение
В нашем примере создайте файл с именем linktargetfinder@robertnyman.com без какого-либо расширения, и в нем просто укажите его, где вы будете иметь свой код, например C:\extensions\ (Windows) или
Создание структуры папок и файлов
Чтобы создать хорошую основу для разработки расширений, необходимо создать структуру кода расширения. Начните с создания этой иерархии:
[Img_assist | NID = 7650 | название = | убывание = | ссылка = нет | Align = нет | ширина = 156 | Высота = 166]
я nstall.rdf
В Description узле
В Description/em:targetApplication узле
c hrome.manifest
Хром Firefox — это все вокруг окна контента. т. е. панель инструментов веб-браузера, меню, строка состояния и т. д. Следующий файл для нашего расширения, который, вероятно, будет неудобно редактировать, — это chrome.mainfest файл. Это, однако, связано с install.rdf ключом к тому, как ваше расширение будет добавлено в Firefox и как оно будет работать. Наш chrome.manifest файл выглядит так:
Итак, что же это за варианты? Давайте пройдемся по ним:
папка c hrome
Хорошо, как только обязательные части убраны, теперь вещи начинают становиться интересными. Это также когда мы начинаем смотреть на XUL, что означает XML User Interface Language. Он разработан Mozilla для создания интерфейсов в Firefox, Thunderbird и т. Д.
Сначала в chrome/content папке создайте три файла: