Как написать скрипт для chrome

В конце 2020 года мы делали проект со снежинками — писали специальный скрипт, который запускал падающий снег на сайтах. Если бы мы хотели сделать такой снег на любом своём сайте, это не составило бы труда: добавляешь скрипт в код страницы, и готово.

А вот на чужих сайтах была проблема. Скрипт нужно было вставлять через консоль. А если на сайте была настроена политика безопасности, которая запрещает запуск внешних скриптов, то магия не срабатывала. 

Сегодня мы это исправим — сделаем расширение для браузера, которое может запускать любой скрипт на любой странице. Мы охватим принцип, на основе которого вы сможете сделать собственные расширения, в том числе намного более сложные. 

👉 Что такое расширение

Расширение для Chrome — это небольшая программа, которая выполняется внутри браузера и помогает расширить возможности сайтов. Сила расширения в том, что оно может выполняться прямо из меню браузера и не зависит от политик безопасности. 

Примеры того, что может сделать расширение: 

  • Сохранить ваши пароли и другие личные данные,
  • Выдрать из страницы скрытые данные (например, ссылку на скачивание музыки).
  • Менять что-то на странице, например, отключать рекламу, переводить текст, удалять ненужное.
  • Собирать статистику вашей работы в интернете.
  • Подключаться к другим сервисам (почте, чатам, файлообменникам) и взаимодействовать с ними из браузера.

В этой статье

Мы сделаем самое простое расширение для браузера Chrome, которое позволит запускать скрипт со снежинками на любом сайте, независимо от настроенной политики безопасности. Для этого воспользуемся официальным руководством Google по созданию расширений.

Манифест

В каждом расширении для браузера должен быть манифест — документ, в котором написано:

  • как называется расширение;
  • к чему расширение может получить доступ;
  • какие скрипты будут работать в фоне;
  • как должна выглядеть иконка расширения;
  • что показать или что выполнить, когда пользователь нажмёт на иконку расширения.

Манифест задаёт общие правила для всего расширения, поэтому манифест — единственный обязательный компонент. Можно обойтись без иконок и скриптов, но манифест обязательно должен быть.Каждый манифест хранится в файле manifest.json — создадим пустой файл с таким именем и напишем внутри такое:

{
«name»: «Запускаем снежинки на любом сайте»,
«description»: «Проект журнала Код»,
«version»: «1.0»,
«manifest_version»: 3
}

Первые две строчки — это название и подробное описание расширения. Третья отвечает за номер версии расширения, а последняя говорит браузеру, какая версия манифеста используется в описании. На момент выхода статьи в феврале 2021 года используется третья версия.

Сохраняем файл и всё, расширение готово. Оно ничего не умеет, ничего не делает, зато мы уже можем добавить его в браузер. Для этого запускаем Хром и в адресной строке пишем:

chrome://extensions/

Мы попадаем на страницу, которая нам покажет все установленные расширения:

Делаем своё расширение для браузера за 10 минут

Чтобы добавить своё расширение, в правом верхнем углу включаем режим разработчика, а затем нажимаем «Загрузить распакованное расширение»:

Делаем своё расширение для браузера за 10 минут

Теперь выбираем папку, в которой лежит наш манифест:

Делаем своё расширение для браузера за 10 минут

Отлично, мы только что добавили в браузер новое расширение:

Делаем своё расширение для браузера за 10 минут

Теперь мы можем обновлять наш манифест, класть в ту же папку дополнительные файлы, а для обновления в браузере достаточно будет нажать на круглую стрелку на карточке расширения.

Чтобы было проще работать и тестировать расширение, закрепим его на панели браузера:

Делаем своё расширение для браузера за 10 минут

Иконки

У расширения есть две иконки, которыми мы можем управлять: 

  1. Картинка в карточке расширения на странице настроек.
  2. Иконка на панели браузера.

Чтобы не рисовать всё с нуля, скачаем папку с иконками из того же руководства Google и положим её в ту же папку, что и манифест:

Теперь добавим иконки в манифест. За картинку в карточке отвечает блок icon, а за иконку на панели —  блок action. Разные размеры картинки нужны для того, чтобы на разных мониторах с любой плотностью пикселей иконки выглядели хорошо:

{
  "name": "Запускаем снежинки на любом сайте",
  "description": "Проект журнала Код",
  "version": "1.0",
  "manifest_version": 3,

  "action": {
    "default_icon": {
      "16": "/images/get_started16.png",
      "32": "/images/get_started32.png",
      "48": "/images/get_started48.png",
      "128": "/images/get_started128.png"
    }
  },
  "icons": {
    "16": "/images/get_started16.png",
    "32": "/images/get_started32.png",
    "48": "/images/get_started48.png",
    "128": "/images/get_started128.png"
  }
}

Сохраняем манифест, обновляем расширение на странице настроек и смотрим результат:

Добавляем иконки в манифест

Настраиваем разрешения

Разрешения — это то, что браузер позволяет делать расширению со страницами и с их содержимым. Для запуска снежинок нам нужно сделать две вещи:

  1. Понять, какая вкладка сейчас активная, чтобы запустить снежинки именно на ней.
  2. Запустить наш скрипт со снежинками.

Чтобы получить доступ к активной вкладке и к запуску скриптов, добавим в манифест такую строку:

"permissions": ["activeTab", "scripting"],

Показываем меню

Если мы сейчас нажмём на иконку расширения на панели браузера, то ничего не призойдёт, потому что мы ничего не запрограммировали. Исправим это — сделаем так, чтобы при нажатии расширение показывало кнопку запуска. Когда расширение будет уметь больше, вместо одной кнопки можно будет показать целое меню.

Чтобы сделать всплывающее меню, добавим в манифест в раздел action такую строку:

    "default_popup": "popup.html",

Она означает, что при нажатии на иконку мы увидим рядом с ней мини-страничку, на которой что-то будет.Создадим в той же папке расширения файл popup.html и добавим в него такой код:

<!DOCTYPE html>
<html lang="ru">
  <head>
  	<meta charset="UTF-8">
    <style type="text/css">

    	/* задаём размеры кнопки и размер текста на кнопке  */
    	button {
	      font-size: 12px;
		  height: 40px;
		  width: 80px;
		}
    </style>
  </head>
  <body>
  	<!-- создаём кнопку на странице -->
    <button id="snow">Запустить снежинки</button>
    <!-- подключаем скрипт, который обработает нажатие на эту кнопку -->
    <script src="popup.js"></script>
  </body>
</html>

Чтобы браузер не ругался, что у нас нет файла popup.js, создадим пустой файл с таким названием и положим его в ту же папку:

Показываем меню расширения

Сохраняем манифест, обновляем его на странице настроек и видим, что у нашего расширения появилось меню с кнопкой:

Показываем меню расширения

Запускаем снежинки

Вся магия будет происходить в файле popup.js — откроем его и добавим такой код:

// получаем доступ к кнопке
let snow = document.getElementById("snow");
// когда кнопка нажата — находим активную вкладку и запускаем нужную функцию
snow.addEventListener("click", async () => {
  // получаем доступ к активной вкладке
  let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  // выполняем скрипт
  chrome.scripting.executeScript({
  	// скрипт будет выполняться во вкладке, которую нашли на предыдущем этапе
    target: { tabId: tab.id },
    // вызываем функцию, в которой лежит запуск снежинок
    function: snowFall,
  });
});

// запускаем снег
function snowFall() {
}

Последнее, что нам осталось сделать, — положить в функцию snowFall() полный код скрипта из проекта со снежинками и сохранить файл.

Проверка

В прошлый раз мы не смогли запустить скрипт на любой странице Яндекса — мешала политика безопасности. Теперь всё работает:

Проверяем расширение

Скачать упакованное расширение. Перед установкой его нужно распаковать в любую папку.

Manifest

To use the chrome.scripting API, declare the "scripting" permission in the manifest plus the host permissions for the pages to inject scripts into. Use the "host_permissions" key or the activeTab permission, which grants temporary host permissions. The following example uses the activeTab permission.

{
"name": "Scripting Extension",
"manifest_version": 3,
"permissions": ["scripting", "activeTab"],
...
}

Usage

You can use the chrome.scripting API to inject JavaScript and CSS into websites. This is similar to what you can do with content scripts. But by using the chrome.scripting namespace, extensions can make decisions at runtime.

Injection targets

You can use the target parameter to specify a target to inject JavaScript or CSS into.

The only required field is tabId. By default, an injection will run in the main frame of the specified tab.

function getTabId() { ... }

chrome.scripting
.executeScript({
target : {tabId : getTabId()},
files : [ "script.js" ],
})
.then(() => console.log("script injected"));

To run in all frames of the specified tab, you can set the allFrames boolean to true.

function getTabId() { ... }

chrome.scripting
.executeScript({
target : {tabId : getTabId(), allFrames : true},
files : [ "script.js" ],
})
.then(() => console.log("script injected in all frames"));

You can also inject into specific frames of a tab by specifying individual frame IDs. For more information on frame IDs, see the chrome.webNavigation API.

function getTabId() { ... }

chrome.scripting
.executeScript({
target : {tabId : getTabId()},
frameIds : [ frameId1, frameId2 ],
files : [ "script.js" ],
})
.then(() => console.log("script injected on target frames"));

You cannot specify both the frameIds and allFrames properties.

Injected code

Extensions can specify the code to be injected either via an external file or a runtime variable.

Files

Files are specified as strings that are paths relative to the extension’s root directory. The following code will inject the file script.js into the main frame of the tab.

function getTabId() { ... }

chrome.scripting
.executeScript({
target : {tabId : getTabId()},
files : [ "script.js" ],
})
.then(() => console.log("injected script file"));

Runtime functions

When injecting JavaScript with scripting.executeScript(), you can specify a function to be executed instead of a file. This function should be a function variable available to the current extension context.

function getTabId() { ... }
function getTitle() { return document.title; }

chrome.scripting
.executeScript({
target : {tabId : getTabId()},
func : getTitle,
})
.then(() => console.log("injected a function"));

function getTabId() { ... }
function getUserColor() { ... }

function changeBackgroundColor() {
document.body.style.backgroundColor = getUserColor();
}

chrome.scripting
.executeScript({
target : {tabId : getTabId()},
func : changeBackgroundColor,
})
.then(() => console.log("injected a function"));

You can work around this by using the args property:

function getTabId() { ... }
function getUserColor() { ... }
function changeBackgroundColor(backgroundColor) {
document.body.style.backgroundColor = backgroundColor;
}

chrome.scripting
.executeScript({
target : {tabId : getTabId()},
func : changeBackgroundColor,
args : [ getUserColor() ],
})
.then(() => console.log("injected a function"));

Runtime strings

If injecting CSS within a page, you can also specify a string to be used in the css property. This option is only available for scripting.insertCSS(); you can’t execute a string using scripting.executeScript().

function getTabId() { ... }
const css = "body { background-color: red; }";

chrome.scripting
.insertCSS({
target : {tabId : getTabId()},
css : css,
})
.then(() => console.log("CSS injected"));

Handling results

The results of executing JavaScript are passed to the extension. A single result is included per-frame. The main frame is guaranteed to be the first index in the resulting array; all other frames are in a non-deterministic order.

function getTabId() { ... }
function getTitle() { return document.title; }

chrome.scripting
.executeScript({
target : {tabId : getTabId(), allFrames : true},
func : getTitle,
})
.then(injectionResults => {
for (const {frameId, result} of injectionResults) {
console.log(`Frame ${frameId} result:`, result);
}
});

scripting.insertCSS() does not return any results.

Promises

If the resulting value of the script execution is a promise, Chrome will wait for the promise to settle and return the resulting value.

function getTabId() { ... }
async function addIframe() {
const iframe = document.createElement("iframe");
const loadComplete =
new Promise(resolve => iframe.addEventListener("load", resolve));
iframe.src = "https://example.com";
document.body.appendChild(iframe);
await loadComplete;
return iframe.contentWindow.document.title;
}

chrome.scripting
.executeScript({
target : {tabId : getTabId(), allFrames : true},
func : addIframe,
})
.then(injectionResults => {
for (const frameResult of injectionResults) {
const {frameId, result} = frameResult;
console.log(`Frame ${frameId} result:`, result);
}
});

Examples

Unregister all dynamic content scripts

The following snippet contains a function that unregisters all dynamic content scripts the extension has previously registered.

async function unregisterAllDynamicContentScripts() {
try {
const scripts = await chrome.scripting.getRegisteredContentScripts();
const scriptIds = scripts.map(script => script.id);
return chrome.scripting.unregisterContentScripts(scriptIds);
} catch (error) {
const message = [
"An unexpected error occurred while",
"unregistering dynamic content scripts.",
].join(" ");
throw new Error(message, {cause : error});
}
}

Unregistering content scripts will not remove scripts or styles that have already been injected.

executeScript

chrome.scripting.executeScript(
  injection:
ScriptInjection,
  callback?:
function,

)

Injects a script into a target context. The script will be run at document_idle. If the script evaluates to a promise, the browser will wait for the promise to settle and return the resulting value.

Parameters

  • The details of the script which to inject.

  • callback

    function optional

    The callback parameter looks like:
    (results:
    InjectionResult[])
    =>

    void

Returns

  • Promise<InjectionResult[]>

    Promises are supported in Manifest V3 and later, but callbacks are provided for backward compatibility. You cannot use both on the same function call. The promise resolves with the same type that is passed to the callback.

getRegisteredContentScripts

chrome.scripting.getRegisteredContentScripts(
  filter?:
ContentScriptFilter,
  callback?:
function,

)

Returns all dynamically registered content scripts for this extension that match the given filter.

Parameters

  • filter

    ContentScriptFilter optional

    An object to filter the extension’s dynamically registered scripts.

  • callback

    function optional

    The callback parameter looks like:
    (scripts:
    RegisteredContentScript[])
    =>

    void

Returns

  • Promises are supported in Manifest V3 and later, but callbacks are provided for backward compatibility. You cannot use both on the same function call. The promise resolves with the same type that is passed to the callback.

insertCSS

chrome.scripting.insertCSS(
  injection:
CSSInjection,
  callback?:
function,

)

Inserts a CSS stylesheet into a target context. If multiple frames are specified, unsuccessful injections are ignored.

Parameters

  • The details of the styles to insert.

  • callback

    function optional

    The callback parameter looks like:
    ()
    =>

    void

Returns

  • Promises are supported in Manifest V3 and later, but callbacks are provided for backward compatibility. You cannot use both on the same function call. The promise resolves with the same type that is passed to the callback.

registerContentScripts

chrome.scripting.registerContentScripts(
  scripts:
RegisteredContentScript[],
  callback?:
function,

)

Registers one or more content scripts for this extension.

Parameters

  • Contains a list of scripts to be registered. If there are errors during script parsing/file validation, or if the IDs specified already exist, then no scripts are registered.

  • callback

    function optional

    The callback parameter looks like:
    ()
    =>

    void

Returns

  • Promises are supported in Manifest V3 and later, but callbacks are provided for backward compatibility. You cannot use both on the same function call. The promise resolves with the same type that is passed to the callback.

removeCSS

chrome.scripting.removeCSS(
  injection:
CSSInjection,
  callback?:
function,

)

Removes a CSS stylesheet that was previously inserted by this extension from a target context.

Parameters

  • The details of the styles to remove. Note that the css, files, and origin properties must exactly match the stylesheet inserted through insertCSS. Attempting to remove a non-existent stylesheet is a no-op.

  • callback

    function optional

    The callback parameter looks like:
    ()
    =>

    void

Returns

  • Promises are supported in Manifest V3 and later, but callbacks are provided for backward compatibility. You cannot use both on the same function call. The promise resolves with the same type that is passed to the callback.

unregisterContentScripts

chrome.scripting.unregisterContentScripts(
  filter?:
ContentScriptFilter,
  callback?:
function,

)

Unregisters content scripts for this extension.

Parameters

  • filter

    ContentScriptFilter optional

    If specified, only unregisters dynamic content scripts which match the filter. Otherwise, all of the extension’s dynamic content scripts are unregistered.

  • callback

    function optional

    The callback parameter looks like:
    ()
    =>

    void

Returns

  • Promises are supported in Manifest V3 and later, but callbacks are provided for backward compatibility. You cannot use both on the same function call. The promise resolves with the same type that is passed to the callback.

updateContentScripts

chrome.scripting.updateContentScripts(
  scripts:
RegisteredContentScript[],
  callback?:
function,

)

Updates one or more content scripts for this extension.

Parameters

  • Contains a list of scripts to be updated. A property is only updated for the existing script if it is specified in this object. If there are errors during script parsing/file validation, or if the IDs specified do not correspond to a fully registered script, then no scripts are updated.

  • callback

    function optional

    The callback parameter looks like:
    ()
    =>

    void

Returns

  • Promises are supported in Manifest V3 and later, but callbacks are provided for backward compatibility. You cannot use both on the same function call. The promise resolves with the same type that is passed to the callback.


June 27, 2014

javascript userjs frontend Chromium browser russian

В интернете много сайтов на которых реализована возможность пролистывать страницу вверх без помощи мыши или полосы прокрутки. Но в то же время еще есть сайты где такой реализации нет. «Почему бы не попробовать написать скрипт который бы добавлял такую кнопку на все сайты?» — подумал я и принялся за реализацию. Такие скрипты называются пользовательскими и имеют расширение *.user.js. Почитать к примеру можно на хабре. К сожалению без «подводных камней» не обходится. Существуют некоторые различия в реализации userjs под разные браузеры. Я остановлюсь на описании реализации нашего userjs скрипта как расширения для браузера Google Chrome.

manifest.json

Обязательным для расширения Google Chrome является файл manifest.json в котором описываются параметры, пути к внешним файлам (*.js, *.css и др.), поддержка версии и т.п. для расширения. Подробнее о файле можно почитать тут. В нашем случае файл manifest.json выглядит следующим образом:

{
    "manifest_version": 2,
    "content_scripts": [ {
        "exclude_globs":    [  ],
        "include_globs":    [ "*" ],
        "js":               [ "jquery.js", "backTopUserJS.user.js" ],
        "css":              [ "css/style.css" ],
        "matches":          [   "http://*/*",
                                "https://*/*"
                            ],
        "run_at": "document_end"
    } ],
    "converted_from_user_script": true,
    "description":  "Back top userscript extension for google chrome",
    "name":         "backTopUserJS",
    "version":      "1"
}

Для удобства мы используем библиотеку JQuery которую положили рядом с файлом manifest.json и основным файлом скрипта (в нашем случае это backTopUserJS.user.js). Его содержимое следующее:

// ==UserScript==
// @name backTopUserJS
// @author Aleksandr Filatov
// @license MIT
// @version 1.0
// ==/UserScript==

function main() {
    var disp = $(window).scrollTop() > 400 ? 'block' : 'none';
    var $upButton = $('<div class="up-button" title="Наверх" style="display:' + disp + '">Наверх</div>');

    $(document).find('body').append($upButton);
    $upButton.click(function () {
        $('html, body').animate({ scrollTop: 0 }, 'slow');
    });

    $(window).scroll(function () {
        if ($(window).scrollTop() > 400) $upButton.fadeIn('slow');
        else $upButton.fadeOut('slow');
    });
};

var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ main +')();'));
(document.body || document.head || document.documentElement).appendChild(script);

Думаю скрипт достаточно понятен, чтобы его подробно рассматривать. Пояснить стоит лишь последние 3 строчки. На самом деле это мелкий хак, который вставляет наш скрипт в код страницы сайта. Если кто-то из вас найдет способ лучше можете написать к комментах свои фиксы :)

Установка расширения в браузере

В Google Chrome можно устанавливать пользовательские скрипты, аналогично другим браузерам, но поскольку Google печется о нашей с вами безопасности, то их приходится оборачивать в расширения для браузера. Разберем установку по шагам.

Создаем папку для нашего расширения например так C:MyChromeExtensionUserJS

Для каждого расширения создаем свою директорию например в нашем случае назовем ее так C:MyChromeExtensionUserJSbackTopUserJS. Добавляем файлы расширения в эту директорию.

Заходим «О браузере Google Chrome» -> вкладка «Расширения» или в адресной строке пишем chrome://extensions/

Ставим галочку «Режим разработчика»
developer_mode

Нажимаем кнопку «Загрузить распакованное расширение» и выбираем директорию нашего расширения. Нажимаем «OK».

Расширение установлено и готово к работе. Чтобы обновить расширения после того как вы внесли в него изменения достаточно просто нажать кнопку «Обновить расширение» или в режиме разработчика сочетание клавиш Ctrl+R.

Итог

Посмотреть исходники скрипта можно на github. Скрипт не претендует на звание идеального, а служит лишь примером и толчком к написанию собственных пользовательских скриптов для Google Chrome.

Web Development

Написать расширение для Chrome непросто. Это не то же самое, что разработка веб-приложения: не хочется перегружать браузер оверхедом JS, ведь расширения работают одновременно с сайтами. Более того, у нас нет инструментов упаковки или отладки из привычных фреймворков.

Skillfactory.ru

Когда я решил заняться созданием расширения для Chrome, то обнаружил: блог-постов и статей об этом довольно мало. И информации оказывается даже ещё меньше, если вам захочется использовать новые инструменты, например TailwindCSS.

В этом руководстве мы напишем расширение для Сhrome с помощью Parcel.js для упаковки и просмотра результатов, а также TailwindCSS для оформления. Кроме того, мы отделим стилизацию расширения от веб-сайта, чтобы избежать конфликта CSS.

Есть несколько типов расширений для Chrome, достойных упоминания:

  • Скрипты содержимого. Наиболее распространённый тип. Они запускаются в контексте веб-страницы и могут изменять её. Именно такое расширение мы и будем создавать.
  • Выпадающее окно (popup). Использует иконку справа от адресной строки, чтобы открыть окно с каким-то HTML.
  • UI с опциями. Пользовательский интерфейс для настройки параметров в качестве расширения. Получить доступ к нему можно, щелкнув правой кнопкой мыши по значку расширения и выбрав пункт “Параметры” или перейдя на страницу расширения из списка расширений Chrome: chrome://extensions.
  • Расширение DevTools. Добавляет функциональность в инструменты разработчика. Оно может добавлять новые панели интерфейса, взаимодействовать с проверяемой страницей, получать информацию о сетевых запросах и многое другое  —  документация Google Chrome.

В этом руководстве мы напишем расширение, используя исключительно скрипты содержимого, отображая содержимое на веб-странице и взаимодействуя с DOM.

Упаковываем расширение с Parcel.js V2

Parcel.js  —  это упаковщик веб-приложений, не требующий конфигурации. Входным может быть файл любого типа, упаковщик прост в использовании и работает с любыми приложениями, включая расширения Chrome. Создаём папку demo-extension . Удостоверьтесь, что у вас установлены yarn или npm. Здесь будем работать с yarn. Установим Parcel как локальную зависимость и создадим папку src

mkdir demo-extension && cd demo-extension && 
льyarn init -y

yarn add -D [email protected]

mkdir src

Добавляем манифест

Каждому браузерному расширению необходим файл манифеста. Именно там мы определяем версию и метаданные расширения, а также скрипты, которые в нём работают. Контент, фон, всплывающее окна, разрешения, если они нужны и так далее. Вы найдёте полное описание файла манифеста в документации Chrome: https://developer.chrome.com/extensions/manifest. Давайте двинемся дальше и добавим в src файл manifest.json с такими строками:

{
  "name": "Demo extension",
  "description": "An extension built with Parcel and TailwindCSS.",
  "version": "1.0",
  "manifest_version": 2,
}

Прежде чем углубиться в детали работы расширения Chrome, установим и настроим TailwindCSS.

Подключаем TailwindCSS

TailwindCSS  —  это CSS-фреймворк, применяющий служебные классы низкого уровня для создания переиспользуемых и настраиваемых компонентов интерфейса. Tailwind устанавливается двумя способами, самый распространённый —  установка с помощью NPM. Кроме того, сразу же стоит добавить autoprefixer и postcss-import:

yarn add tailwindcss

yarn add -D autoprefixer postcss-import

Они нужны, чтобы добавить префиксы поставщиков к стилям и иметь возможность писать конструкции @import "tailwindcss/base", импортируя файлы Tailwind прямо из node_modules.

Skillfactory.ru

Теперь, когда всё установлено, давайте создадим файл postcss.config.js в корневом каталоге. Этот файл  —  конфигурация для PostCSS. Вставим в него такой код:

module.exports = {
  plugins: [
    require("postcss-import"),
    require("tailwindcss"),
    require("autoprefixer"),
  ],
};

Порядок плагинов здесь имеет значение! Это всё, что нужно, чтобы начать использовать TailwindCSS в вашем расширении. Начинаем. Создадим файл style.css в папке src и импортируем в него стили Tailwind:

@import "tailwindcss/base"; 
@import "tailwindcss/utilities";

Очищаем CSS с помощью PurgeCSS

Убедимся, что мы импортируем только те стили, которые используем, включив очистку. Создадим конфигурационный файл Tailwind, запустив такую команду:

$ npx tailwindcss init

Теперь у нас есть tailwind.config.js. Чтобы удалить неиспользуемый CSS, добавляем пути ко всем нашим файлам JS в поле конфигурации purge:

module.exports = {
  purge: [
    './src/**/*.js', ?
  ],
  theme: {},
  variants: {},
  plugins: [],
}

Теперь CSS будут очищены, а неиспользуемые стили удалены при сборке для продакшна.

Включаем горячую перезагрузку

Chrome не перезагружает файлы при внесении изменении, то есть нам нужно нажимать кнопку “Перезагрузить” на странице расширений каждый раз, когда мы хотим посмотреть на результат. К счастью, есть пакет NPM для автоматической перезагрузки:

$ yarn add crx-hotreload

Чтобы использовать его, создадим файл background.js в папке src и импортируем в этот файл crx-hotreload:

import "crx-hotreload";

Наконец, добавим указатель на background.js в manifest.json, чтобы он мог работать с нашим расширением: горячая перезагрузка в продакшне отключена по умолчанию:

{
  "name": "Demo extension",
  "description": "An extension built with Parcel and TailwindCSS.",
  "version": "1.0",
  "manifest_version": 2,
  "background": { ?
    "scripts": ["background.js"]
  },
}

Достаточно конфигураций. Давайте создадим небольшую форму-скрипт в расширении.

Типы скриптов расширения Chrome

Как уже упоминалось, у расширений Chrome есть несколько типов скриптов:

  • Скрипты содержимого —  это сценарии, которые выполняются в контексте посещаемой веб-страницы. Вы можете запустить любой код JavaScript, в противном случае доступный на любой обычной веб-странице, включая доступ к DOM и манипулирование им.
  • Фоновые скрипты  —  это место, где вы можете реагировать на события браузера с доступом к API расширения.

Добавляем скрипт содержимого

Создадим файл content-script.js в папке src. И добавим HTML-форму в только что созданный файл:

import cssText from "bundle-text:../dist/style.css";

const html =
`
<style>${cssText}</style>

<section id="popup" class="font-sans text-black z-50 w-full fixed top-0 right-0 shadow-xl new-event-form bg-white max-w-sm border-2 border-black p-5 rounded-lg border-b-6">
  <header class="flex mb-5 pl-1 items-center justify-between">
    <span class="text-2xl text-black font-extrabold">New event!</span>
  </header>
  <main class="event-name-input mb-6">
    <div class="mb-6">
      <label
        for="event-name"
        class="font-bold pl-1 block mb-1 text-black text-xl"
        >
      Event name
      </label>
      <div class="duration-400 flex bg-white border-black border-2 rounded-lg py-4 px-4 text-black text-xl focus-within:shadow-outline">
<input
          id="event-name"
          name="event-name"
          type="text"
          placeholder="web.dev LIVE"
          class="font-medium w-full focus:outline-none"
          />
      </div>
    </div>
    </div>
    <div class="mb-6">
      <label
        for="event-date"
        class="font-bold pl-1 block mb-1 text-black text-xl"
        >
      Date
      </label>
      <div class="event-date-input duration-400 border-black flex bg-white border-2 rounded-lg py-4 px-4  text-xl focus-within:shadow-outline">
        <input
          id="event-date"
          name="event-date"
          type="date"
          class="font-medium w-full focus:outline-none"
          />
      </div>
    </div>
    <div class=" mb-8">
    <label
      for="event-time-input"
      class="font-bold pl-1 block mb-1  text-xl"
      >
    Time
    </label>
    <div class="inline-flex items-center">
      <input
        id="event-time-input"
        type="time"
        value="17:30"
        class="border-black mr-4 lowercase duration-400 w-auto bg-white text-xl border-2  rounded-lg px-4 py-4 focus:outline-none focus:shadow-outline"
        />
      <div class="inline-flex flex-col">
        <span class="text-xl font-bold">Casablanca</span>
        <span class="text-base font-normal">Africa</span>
      </div>
    </div>
  </main>
  <footer>
  <button 
    class="duration-400 bg-green-400 text-xl py-4 w-full rounded-lg border-2 border-b-6 leading-7 font-extrabold border-black focus:outline-none focus:shadow-outline"
    >
  Save </button>
  </footer
</section>
`

const shadowHost = document.createElement("div");
document.body.insertAdjacentElement("beforebegin", shadowHost);
const shadowRoot = shadowHost.attachShadow({ mode: 'open' });

shadowRoot.innerHTML = html

Оформление стилей браузерного расширения сложнее, чем кажется. Нужно убедиться, что ваши стили не влияют на стили веб-сайта. Применим Shadow DOM для решения этой проблемы. 

Теневой DOM  —  мощная техника инкапсуляции стилей: область применения стиля ограничивается теневым деревом. Таким образом ничего не просачивается на веб-страницу. Кроме того, внешние стили не переопределяют содержимое дерева, хотя переменные CSS всё ещё доступны.

Теневой хост  —  это любой элемент DOM, к которому мы хотели бы присоединить теневое дерево. Теневой корень  —  это то, что возвращается из attachShadow, а его содержимое  —  то, что визуализируется.

Будьте осторожны: единственный способ стилизовать содержимое теневого дерева  —  встроить стили. Parcel V2 из коробки есть функция, благодаря которой вы можете импортировать содержимое одного пакета и использовать его в качестве скомпилированного текста внутри ваших файлов JavaScript. Именно это мы и сделали со своим пакетом style.css. Parcel заменит его во время упаковки.

 Теперь мы можем автоматически встроить CSS в Shadow DOM во время сборки. Конечно, мы должны сообщить браузеру о файле content-script.js, в котором встраивается style.css. Для этого включаем скрипт содержимого в манифест. Обратите внимание на секцию content-scripts ниже первого блока:

{
  "name": "Demo extension",
  "description": "An extension built with Parcel and TailwindCSS.",
  "version": "1.0",
  "manifest_version": 2,
  "background": {
    "scripts": ["background.js"]
  },

  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content-script.js"],
    }
  ]
}

Чтобы обслуживать наше расширение, добавим несколько скриптов к package.json:

"scripts": {
    "prebuild": "rm -rf dist .cache .parcel-cache",
    "build:tailwind": "tailwindcss build src/style.css -c ./tailwind.config.js -o dist/style.css",
    "watch": "NODE_ENV=development yarn build:tailwind && cp src/manifest.json dist/ && parcel watch --no-hmr src/{background.js,content-script.js}",
    "build": "NODE_ENV=production yarn build:tailwind && cp src/manifest.json dist/ && parcel build src/{background.js,content-script.js}",
  }

Наконец, запускаем yarn watch, переходим в chrome://extensions и убеждаемся, что в правом верхнем углу страницы включен режим разработчика. Нажмите на кнопку “Загрузить распакованный” и выберите папку dist в разделе demo-extension.

Если вы получили ошибку Error: Bundles must have unique filePaths, ее можно исправить, просто удалив main из package.json .

Подготовка к публикации

Прежде чем углубляться в эту тему, давайте добавим новый скрипт в конфигурацию NPM, который поможет сжать файлы расширения в соответствии с требованиями Chrome.

"scripts": {
    "prebuild": "rm -rf dist .cache .parcel-cache",
    "build:tailwind": "tailwindcss build src/style.css -c ./tailwind.config.js -o dist/style.css",
    "watch": "NODE_ENV=development yarn build:tailwind && cp src/manifest.json dist/ && parcel watch --no-hmr src/{background.js,content-script.js}",
    "build": "NODE_ENV=production yarn build:tailwind && cp src/manifest.json dist/ && parcel build src/{background.js,content-script.js}",
  }

Если у вас ещё не установлен zip, пожалуйста, выполните команду:

  • На MacOS: brew install zip.
  • На Linux: sudo apt install zip.
  • На Windows: powershell Compress-Archive -Path .\dist\ -Destination .\chrome-extension.zip.

Теперь всё, что остается,  —  это отправиться в Chrome Web Store Developer Dashboard  —  панель управления разработчика, чтобы настроить учетную запись и опубликовать своё расширение.

Полную версию этого туториала вы найдёте в моём аккаунте Github.

Заключение

Расширения Chrome, в конечном счёте, не так уж сильно отличаются от веб-приложений. Сегодня мы написали расширение с применением новейших технологий и практик в веб-разработке. Надеюсь, это руководство поможет вам немного ускорить разработку вашего расширения!

Читайте также:

  • 10 правил проектирования взаимодействия, которые нельзя нарушать
  • 10 распространенных ошибок UI-дизайнеров
  • Руководство разработчика по оптимизации скорости работы веб-сайтов

Перевод статьи Marouane Rassili: “How to Make a Chrome Extension — a Browser Plugin Development Tutorial”

Написать расширение для google chrome несложно. Но при написании первого раширения могут возникнуть (и возникают) вопросы. Большинство мануалов по написанию первого расширения расчитаны на использования манифеста первой версии, поддержка которого в скором будущем прекратится.

В этой статье будет рассмотрено:

  • Как составлять манифест v.2
  • Как работать с удаленными ресурсами
  • Как работать с cookies
  • Как работать с local storage
  • Как работать с уведомлениями

Введение

К концу статьи у нас будет готово расширение-органайзер, в котором будет поле для добавления новой задачи, а так же список задач на текущий день. Обозначим все требования к органайзеру:

  • Должен иметь поле для добавления события (дата, время, событие)
  • Должен отображать все задачи на текущий день, отсортированные по времени
  • Все прошедшие события должен отображать зачеркнутыми
  • Должен иметь поле для ввода времени, за сколько надо показывать уведомление, а так же чекбокс разрешающий и запрещающий показывать уведомления
  • За указанное время до события должен отображать уведомление о приближающемся событии

Манифест

Начнем создавать расширение с самого начала, то есть с манифеста. Манифест – это тот самый файл, в котром прописываются все параметры расширения. Название, описание, версия, разрешение на доступ к сайтам, разрешение на использование кук, уведомлений, локального хранилища. В общем, манифест – это мозг расширения. Создаем файл manifest.json. Манифест – единственный файл, который должен иметь заранее предопределенное имя, все остальные файлы можно будет называть как угодно. В этом файле есть три обязательных поля:

manifest.json

{
	“name”:  “Organizer extension”,  // Название расширения
	“version”: “1.0”,  // Версия расширения.
	“manifest_version”: 2 // Версия манифеста
}

Тут есть пара правил:

  • Версия манифеста должна быть целочисленной, то есть должна писаться как 2, а не “2”.
  • Версия расширения должна быть строковой, но содержать только числа и точки, то есть “1.0” — хорошо, а 1.0 и “0.9 beta” — плохо.

С обязательными полями – все, перейдем к созданию всплывающего окна расширения. Для того, чтобы по нажатию на пиктограмму, открывалось окно, необходимо добавить в манифест поле “browser_action”

manifest.json

{
…
"browser_action": {
	"default_title": "Open organizer", // Заголовок. Его видно если навести курсор на иконку в браузере
	"default_icon": "icon_small.png", // Путь к иконке расширения
	"default_popup": "popup.html" // Путь к странице с попапом
}
}

Теперь создадим всплывающее окно. Это обычная html страница, которая может быть любого размера и цвета, никаких фокусов. Назовем файл “popup.html”. Создать этот файл мало – его надо указать в манифесте. Так мы и сделали: «default_popup»: «popup.html».

popup.html

<!DOCTYPE html>
<html>
	<head>
		<title></title>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	</head>
	<body>
		<div>It works!</div>
	</body>
</html>

Добавление расширения в браузер

Теперь пришло время проверить работоспособность нашего расширния. Для этого загрузим расширение в браузер. Открываем в хроме меню расширений. Ставим птицу на “Developer mode”.
Пишем расширение для google chrome
После этого появятся три кнопки. Нажимаем “Load unpacked extension…”. Выбираем папку с файлами расширения. После этого появится наше расширение. Если все правильно, то по нажатию на иконку – повится окно:
Пишем расширение для google chrome

Подключение скриптов

Теперь можно приступить к интересному. Подключим два javascript файла. Первый – popup.js, второй – jquery. С первым проблем не возникнет, но jquery будем подключать не локальный, а удаленный, взятый по адресу ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js. Проблемы возникнут от того, что по умолчанию расширение не имеет доступа к сторонним ресурсам. Чтобы получить доступ, надо его указать в манифесте. Доступ к чему-либо указывается в поле “permissions”. Так же, для удаленных скриптов и css надо указывать доступные удаленные ресурсы.

manifest.json

{
…
"permissions": [
	"https://ajax.googleapis.com/*"
],
"content_security_policy": "script-src 'self' https://ajax.googleapis.com; object-src 'self'"
}

Теперь подключим эти скрипты в popup.html

popup.html

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="popup.js"></script>

Storage

При помощи storage в хроме можно хранить пользовательские данные. И именно в storage наше расширение и будет хранить грядущие события. На то есть две причины. Во-первых, данные, хранищиеся в storage можно синхронизировать, если залогиниться в браузере. А во-вторых, данные можно хранить не только в виде строки, как в cookies, а в любом виде, то есть можно хранить и массивы и объекты. Чтобы это заработало, откроем доступ к storage в манифесте.

manifest.json

{
...
"permissions": [
  …
  "storage"
]
...
}

Теперь переделаем всплавающее окно. Во всплывающем окне будет поле с сегодняшней датой, три инпута для даты, времени и описания нового события, кнопка для добавления нового события, а так же список всех событий на сегодня.

popup.html

<div id="container">
  <div id="today_date">Date</div>
  <table class="table">
    <tr>
      <td>Дата</td>
      <td>Время</td>
      <td>Задача</td>
    </tr>
    <tr>
      <td><input type="text" id="new_date" value="" class="input_date" /></td>
      <td><input type="text" id="new_time" value="" class="input_date" /></td>
      <td><input type="text" id="new_task" value="" class="input_task" /></td>
    </tr>
  </table>
  <ul></ul>
</div>

И сразу же добавим отображение даты в блоке #today_date.

popup.js

$(function(){
  var today = new Date();
  $("#today_date").html(today.getDate()+"."+(parseInt(today.getMonth())+1)+"." + today.getFullYear());
}

Выглядеть должно так:
Пишем расширение для google chrome

Итак, при нажатии на кнопку “+” у нас должно добавляться событие. Вначале файла объявим глобальную переменную storage – объект для работы с storage, а так же глобальный массив tasks для хранения событий.

popup.js

var storage = chrome.storage.sync;
var tasks = new Array();
$(function(){
  …
  $("#add_task").click(function(){
    var new_task = new Object();
    new_task.date = validateField($("#new_date").val(), "date");
    new_task.time = validateField($("#new_time").val(), "time");
    new_task.task = $("#new_task").val();
    if(!new_task.task || !new_task.date || !new_task.task){
       return false;
    }
    tasks[tasks.length] = new_task;
    storage.set({
      tasks:tasks
    });
  });
});

Функция валидации проверяет, что дата записана в формате d.m.yyyy, а время в формате hh:mm, а так же, что в описании события не меньше трех символов.

popup.js

var validateField = function(val, type){
    if(type == "date"){
        var date = val.split(".");
        var year = new Date();
        year = year.getFullYear();
        if(date.length == 3 && parseInt(date[0]) == date[0] && date[0] <= 31 && parseInt(date[1]) == date[1] && date[1] <= 12 && parseInt(date[2]) == date[2] && date[2] >= year){return val;}
    } else if(type == "time"){
        var time = val.split(":");
        if(time.length == 2 && parseInt(time[0]) == time[0] && time[0] < 24 && parseInt(time[1]) == time[1] && time[1] < 60){return val;}
    } else if(type == "task" && type.length >= 3){
        return val;
    }
    return null;
}

С добавлением разобрались, переходим к получению событий на сегодня. Для этого надо получить все события из базы, выбираем из всех только сегодняшние события и сортируем их по времени по возрастанию.

popup.js

$(function(){
  …
  var now_hours = today.getHours() < 10 ? "0" + today.getHours() :  today.getHours();
    var now_minutes = today.getMinutes() < 10 ? "0" + today.getMinutes() : today.getMinutes();
    var now_time = now_hours + "" + now_minutes;
    storage.get("tasks",function(items){
        if(items.tasks){
            tasks = items.tasks;
            var today_tasks = getTodayTasks(tasks);
            if(today_tasks.length >0){
                for(var i in today_tasks){
                    var this_time = today_tasks[i].time.replace(":", "");
                    var add = this_time > now_time ? "" : ' class="done"';
                    var add_html = '<li'+add+'><strong>'+today_tasks[i].time+'</strong> '+today_tasks[i].task+'</li>';
                    $("ul").append(add_html);
                }
            }
        }
    });
  …
});

Функция getTodayTasks() возвращает из общего списка только события с сегодняшней датой.

popup.js

var getTodayTasks = function(tasks){
    var today_tasks = new Array();
    var today = new Date();
    var today_date = today.getDate()+"."+(today.getMonth() + 1 )+ "." + today.getFullYear();
    for(var i in tasks){
        if(tasks[i].date == today_date){
            today_tasks[today_tasks.length] = tasks[i];
        }
    }
    if(today_tasks.length > 0){
        today_tasks = sortTasks(today_tasks);
    }
    return today_tasks;
}

Функция sortTasks() сортирует события по возрастанию времени.

popup.js

var sortTasks = function(tasks){
    if(tasks.length > 0){
        var swapped = true;
        while (swapped) {
            swapped = false;
            for (var i=0; i < tasks.length-1; i++) {
                var this_time = tasks[i].time.replace(":", "");
                var next_time = tasks[i+1].time.replace(":", "");
                if (this_time > next_time) {
                    var temp = tasks[i];
                    tasks[i] = tasks[i+1];
                    tasks[i+1] = temp;
                    swapped = true;
                }
            }
        }
    }
    return tasks;
}

Уведомления

Пришло время настроить отображение уведомлений на экране. Добавим во всплывающее окно специальный чекбокс. Если этот чекбокс будет отмечен – уведомлениея будут показываться, если не будет отмечен – не будут. Так же добавим текстовый инпут. Цифра в этом инпуте будет показывать, за какое время до событя будет показываться уведомление. То есть если у нас событие назначено на 19:00, в этом текстовом инпуте будет 5, значит в 18:55 появится уведомление. Добавим в popup.html код с этими инпутами

popup.html

<div>
  <input type="checkbox" id="show_notifications" checked="checked" />
  <input type="text" id="when_to_notify" class="input_date" value="" /> 
  Показывать уведомления
</div>

Пишем расширение для google chrome

Теперь давайте разберемся с тем, как это будет работать. При нажатии на чекбокс, будет проверяться его атрибут checked, значение атрибута будет записываться в cookie “show_notifications”. Перейдем к текстовому инпуту. По изменению его значения, новое значение будет валидироваться, если оно целочисленное и не больше 120, записываем новое значение в cookie “when_to_notify”.

Но для того, чтобы у нас это заработало, надо открыть доступ к cookies. Для этого заходим в manifest.json и добавляем в “permissions”

manifest.json

{
...
"permissions": [
	…
	“cookies”
]
...
}

Теперь можно приступать к скрипту. Заходим в popup.js. Для начала установим первоначальные значения в инпутах. По-умолчанию чекбокс не отмечен, то есть уведомления не показываются, а время равно 0. При клике на чекбокс, будет меняться значение cookie “show_notifications”. При изменении значения в тектовом поле, будет меняться значение cookie “when_to_notify”.

popup.js

$(function(){
    setCheckbox();
    setWhenToNotify(getCookie("when_to_notify"));
    ...
    $("#show_notifications").click(function(){
        setCookie("show_notifications", document.getElementById("show_notifications").checked);
    });
    $("#when_to_notify").change(function(){
        setWhenToNotify($(this).val());
    });
});

Рассмотрим подробнее функции. Начнем с функций работы с cookies. В данном случае были взяты готовые функции с w3schools.com.

popup.js

var setCookie = function(c_name,value,exdays){
    /*
     *Взято с http://www.w3schools.com/js/js_cookies.asp
     */
    var exdate=new Date();
    exdate.setDate(exdate.getDate() + exdays);
    var c_value=escape(value) + ((exdays==null) ? "" : "; expires="+exdate.toUTCString());
    document.cookie=c_name + "=" + c_value;
}

var getCookie = function(c_name){Позвонить Васе П.
    /*
     *Взято с http://www.w3schools.com/js/js_cookies.asp
     */
    var i,x,y,ARRcookies=document.cookie.split(";");
    for (i=0;i<ARRcookies.length;i++){
        x=ARRcookies[i].substr(0,ARRcookies[i].indexOf("="));
        y=ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1);
        x=x.replace(/^s+|s+$/g,"");
        if (x==c_name){
            return unescape(y);
        }
    }
}

Разберемся с функцией setCheckbox(). Она получает cookie “show_notifications” и проверяет, если полученное значение равно “true”(текстовое, да), то параметр checked у чекбокса true, иначе false.

popup.js

var setCheckbox = function(){
    var val = getCookie("show_notifications")
    document.getElementById('show_notifications').checked = ((val == "true") ? true : false);
}

Теперь рассмотрим setWhenToNotify(). Она принимает новое значение таймера. Если это значение – целочисленное и не больше 120 минут, то в cookie “when_to_notify” устанавливается новое значение. Если переменная не прошла эту валидацию, то в инпут возвращается предыдущее значение из cookies “when_to_notify”.

popup.js

var setWhenToNotify = function(val){
    var last_val = getCookie("when_to_notify");
    last_val = last_val != "undefined"? last_val: 0;
    val = (parseInt(val)==val && val <=120) ? val : last_val;
    setCookie("when_to_notify", val);
    $("#when_to_notify").val(val);
}

Перейдем к самим уведомлениям. Для этого откроем доступ к уведомлениям и подключим фоновый background.js. Нужно подключить именно фоновый файл, так как если уведомления вызывать из popup.js, то уведомления будут появляться только если открыто всплывающее окно.

manifest.json

{
...
"permissions": [
    …
    "notifications"
    ],
"background": { "scripts": ["background.js"] },
"web_accessible_resources": ["icon_small.png"],
...
}

Последняя строчка дает доступ к удаленному файлу. Дело в том, что картинка, которая отображается в уведомлении обязательно должна быть доступна расширению удаленно. В данном случае файл локальный, но доступ открывать все равно надо. Теперь возьмемся за background.js. Объявим переменную storage и пустой массив tasks. Далее раз в минуту скрипт будет получать список сегодняшних событий и получать из них список задач, которые должны произойти через указанное время. После этого для каждой такой задачи будет показано уведомление.

background.js

var storage = chrome.storage.sync;
var tasks = new Array();
setInterval(function() {
    storage.get("tasks",function(items){
        if(items.tasks && getCookie("show_notifications") == "true"){
            tasks = getTodayTasks(items.tasks);
            if(window.webkitNotifications){
                var texts = getNextTask(tasks);
                for(var i in texts){
                    show(texts[i]);    
                }
            }
        }
    });
}, 60000);

Функции getTodayTasks() и getCookie() взяты из popup.js. Так что начнем разбор с функции getNextTask(). Функция сравнивает текущее время и время события, если оно равно тому значению, которое хранится в cookie “when_to_notify”, то в массив next дописывается строка из времени события и его описания. После проверки всех событий возвращет массив next.

background.js

var getNextTask = function(tasks){
    var now = new Date();
    now = now.getTime();
    var next = new Array();
    for(var i in tasks){
        var date = tasks[i].date.split(".");
        var time = tasks[i].time.split(":");
        var task_date = new Date(parseInt(date[2]), parseInt(date[1]-1), parseInt(date[0]), parseInt(time[0]), parseInt(time[1]), 0, 0);
        task_date = task_date.getTime();
        var delta = Math.round((task_date-now)/60000);
        if(delta == getCookie("when_to_notify")){
            next[next.length] = tasks[i].time + " - " + tasks[i].task;
        }
    }
    return next;
}

Функция show() показывает уведомление с заданным текстом.

background.js

var show = function(text) {
    var notification = window.webkitNotifications.createNotification('icon_small.png','Есть дело', text);
    notification.show();    
}

Результатом работы этого скрипта будет такое вот уведомление:
Пишем расширение для google chrome

Послесловие

Как и обещано, в конце статьи у нас есть готовое расширение-органайзер для Google Chrome.
Теперь добавим расширение в Chrome Web Store. Загружать надо расширение, запакованное в .zip-архив. Для начала заходим в Web Store. Для этого заходим в хроме на вкладку “Приложения” и нажимаем кнопку “Web Strore”
Пишем расширение для google chrome
Теперь заходим в меню. Для этого нажимаем шестиренку и открываем “Developer dashboard”
Пишем расширение для google chrome
Нажимаем большую кнопку “Add new item”. После этого надо будет выбрать zip-архив с расширением нажать “upload”
Пишем расширение для google chrome
Далее надо заполнить небольшую форму с описанием расширения. Теперь есть выбор либо сохранить расширение в черновил либо опубликовать. Просто так опубликовать его не получится так как регистрация в Web Store стоит 5 $. Плату за регистрацию надо платить один раз для аккаунта, а не для каждого расширения. После оплаты появляется позможность опубликовать расширение, но и тут надо быть готовым к тому, что его будут валидировать несколько дней.

Полезные ссылки

Описание полей манифеста: developer.chrome.com/trunk/extensions/manifest.html
Работа с storage: developer.chrome.com/trunk/extensions/storage.html
Информация по уведомлениям: developer.chrome.com/extensions/notifications.html
Исходники расширения: github.com/bozheville/orginaizer_extension

Автор: bozheville

Источник

Google предоставляет подробную документацию о том, как сделать расширение для браузера Chrome. И помните, что браузерное расширения — это просто HTML, CSS и JavaScript. Вы можете добавлять библиотеки и фреймворки или разрабатывать код «старомодным» способом.

  • Настройка
  • Документация
  • Отладка
  • Функционал Eye Rest
  • API
    • Таймеры
    • Фоновые скрипты
  • Другие API
    • Windows
    • Хранилище
    • Declarative Content
  • Расширение
  • Публикация
  • Заключение

Что нужно знать:

  • Обратные вызовы;
  • Таймауты;
  • Инструменты разработчика Chrome.

При создании расширений под Chrome используется большое количество обратных вызовов. Поэтому перед началом я рекомендую освежить в памяти эту тему.

Настройка

Преувеличение

При создании расширения я не учла, что одновременно буду работать с тремя таймерами. И я сэкономила бы много времени, если бы уделила внимание их организации и ознакомлению с документацией.

Обратите внимание, что это руководство описывает только создание собственного пользовательского окна. Но не редактирование существующих окон или вкладок.

Изучите:

  • Руководство Google по расширениям браузера.
  • Руководство по началу работы.
  • Обзор по расширениям Chrome.

Файл manifest.json предоставляет браузеру информацию о расширении. В том числе о том, где расположены файлы и иконки расширения, а также данные для доступа к API. Вот как выглядел мой файл manifest.json:

Документация

https://github.com/jennz0r/eye-rest/blob/master/manifest.json

Изображение, которое описывает архитектуру расширения.

Документация - 2

Файл background.js является обработчиком событий расширения. Он постоянно прослушивает события браузера, которые вы передаете через Chrome Extension API. Google говорит, что эффективный фоновый скрипт загружается только тогда, когда он необходим, и выгружается, когда простаивает.

Popup — это маленькое окно, которое появляется при клике по иконке расширения в меню Chrome. Оно состоит из разметки и скрипта. Вы можете указать браузеру, где его найти, в разделе manifest.json — page_action: { «default_popup»: FILE_NAME_HERE }.

Страница параметров является именно тем, что ожидается. На ней отображаются настраиваемые параметры, выводимые пользователю, только когда он кликает правой кнопкой мыши в меню Chrome и выбирают пункт «Параметры» для расширения. Эта страница также состоит из разметки и скриптов. Вы можете указать браузеру, где ее найти, в разделе options_page: FILE_NAME_HERE файла manifest.json.

Content scripts — это крипты, которые будут взаимодействовать с любыми окнами или вкладками, открытыми пользователем. Они также будут взаимодействовать с вкладками и окнами, открытыми расширением.

Перед началом не забудьте ознакомиться с руководством по отладке!

Как и в любом другом окне Chrome, в расширении вы можете использовать и встроенные инструменты разработчика.

Например, при тестировании расширения я получила ошибку “This request exceeds the MAX_WRITE_OPERATIONS_PER_HOUR quota”. Оказывается, существуют ограничения на синхронизацию хранимой информации.

Еще одна ошибка, которую я продолжала получать: “Alarm delay is less than minimum of 1 minutes. In released .crx, alarm “ALARM_NAME_HERE” will fire in approximately 1 minutes”. Оказывается, есть минимальные интервалы времени для предупреждений.

Старые добрые console.log действительно могут помочь с обратными вызовами и прослушивателями!

Отладка

Я добавила кучу «console.log», пытаясь убрать предупреждения.

Что за расширение я создала? Оно позволяет отдохнуть глазам в течение двадцати секунд каждые двадцать минут.

Схема работы расширения:

  • Если расширение работает,
  • Если пользователь не нажал кнопку «Пауза» во всплывающем окне,
  • Если счетчик во всплывающем окне достиг отметки 00:00, ТОГДА
    • Открывается новое окно с HTML-таймером, И
    • Начинается 20-секундный обратный отсчет в HTML-таймере, И
    • Сбрасывается счетчик всплывающего окна на 20:00.
  • Если HTML-таймер достиг нуля, ТОГДА
    • Закрыть это окно и повторить цикл заново.

Звучит довольно просто, но эти таймеры запутали меня. Чтобы понять суть проблемы, посетите репозиторий GitHub для Eye Rest.

Теперь рассмотрим API, который я использовала для создания этого расширения.

Таймеры Chrome  — это в основном setTimeout и setInterval. Для получения дополнительной информации, ознакомьтесь с документацией.

Одно интересное замечание о таймерах в Chrome — они работают постоянно. Так как освобождение ресурсов памяти реализовано здесь плохо. В этом я убедилась, когда использовала метод clearAll для удаления таймеров, созданных при предыдущих загрузках или установках расширения. Единственный способ решения данной проблемы – указывать уникальное имя таймера каждый раз, когда загружается расширение. А также сбрасывать другие таймеры без этого уникального имени.

В своем расширении Eye Rest используется два фоновых скрипта: прослушиватель событий и файл вспомогательных функций.

Я хотела, чтобы фоновый скрипт очищал все существующие таймеры, создавал новый таймер и добавлял оставшееся время до истечения таймера в локальное хранилище сразу после загрузки расширения.

Чтобы сделать функцию clearAndCreateAlarm доступной для фонового скрипта, я добавила первый элемент helpers.js в background> scripts в файле manifest.json.

Я также хотела, чтобы скрипт всплывающего окна делал то же самое, когда пользователь отключал функционал расширения. Чтобы сделать функцию доступной для всплывающего окна, я размещаю вспомогательный скрипт в HTML-файле вплывающего окна.

Для создания окна таймера используется Windows API. Этот процесс инициируется фоновым скриптом. Я передаю timer.html, type, size, position и другие визуальные опции как параметр URL.

Чтобы передавать информацию между фоновым скриптом и скриптом всплывающего окна, используйте хранилище Chrome или локальное . Одним из преимуществ использования локального хранилища является отсутствие лимитов на операции записи.

Обратный отсчет должен изменяться каждую секунду. Он довольно сложный, и для этого нужно много записей. Вот почему я выбрала вариант локального хранилища. Вы можете увидеть, как я получаю и устанавливаю эти переменные в скриптах Background, Helper и Popup. Найдите в коде date, nextAlarmTime и isPaused.

Declarative Content API позволяет вывести страницу расширения на основе несколько типов сопоставлений без необходимости получать права доступа к хосту Поэтому он нужен нам, чтобы расширение работало в браузере!

Я реализовала это в моем фоновом скрипте. Всплывающее окно расширения должно отображать на каждой просматриваемой странице в браузере.

Расширение

Вот как выглядело мое оригинальное всплывающее окно, прежде чем я добавила стили.

Расширение - 2

И вот как оно выглядит с новыми стилями.

А вот как выглядят окно таймера и всплывающее окно!

Публикация расширения стандартизирована: архивируете файлы, создаете новую или используете существующую учетную запись Google Developer, загружаете файлы, добавляете некоторые данные и платите 5 долларов США.  После этого ваше расширение будет доступно в магазине Chrome. Мое расширение теперь доступно для установки.

Создание этого расширения Chrome стоило мне боли в плечах и уставших глаз. Но теперь Eye Rest может напоминать мне, что нужно делать перерыв каждые 20 минут.

27.06.2014, 08:45. Показов 57589. Ответов 1


1. Что такое расширения к браузерам?
Расширения к браузерам — это программы, которые внедряются в браузер и добавляют в него новые функции или настраивают имеющиеся.
Расширения к браузерам могут:

  • изменять html-код страниц, открываемых в браузере. В том числе внедрять в них скрипт, который на страницах не отображается, но выполняется для каждой странице при её загрузке.
    В хроме такой скрипт называется скриптом содержимого или контентным скриптом, в мазиле и опере — юзерскриптом.
    Примечание: в отличие от обычных видимых скриптов, этот скрипт может делать кроссдоменные запросы XMLHttpRequest.
  • добавлять кнопки на панели инструментов браузера и создавать новые тулбары
  • управлять вкладками браузера: создавать, закрывать, обновлять, посылать по определённому urlу
  • управлять закладками браузера: создавать, изменять, удалять
  • делать редиректы с одних url на другие, причём не только при открытии того или иного url во вкладке, но и при доступе к нему из фреймов, а также через XMLHttpRequest или через script, img, video.
  • просматривать и изменять историю посещенных страниц в браузере
  • и другое, в зависимости от браузера и его версии

Расширения можно публиковать в магазине расширений для соотв. браузера, откуда пользователи смогут их скачивать бесплатно или покупать за деньги.
Ссылки на магазины даны ниже.

Не стоит путать расширения с плагинами.

2. Что представляют собой расширения и как их пишут?
Расширения к браузерам обычно состоят из:

  • файла настроек расширения (ini, json, xml), где указаны, например, название расширения, его версия, автор, поддерживаемые версии браузера, а также файлы скриптов и html страниц, необходимых расширению
  • фонового JS-скрипта (или фоновой страницы с таким скриптом), который выполняется 1 раз при загрузке браузера, не отображается ни на каких страницах, но может менять какие-то настройки в браузере, скажем добавлять кнопку на тулбар
  • юзерскрипта, который внедряется в страницы, не отображается в их коде, но может их изменять так же свободно, как и если бы он был на данном сайте изначально
  • вспомогательных html-страниц, например, страницы настроек для пользователя или диалогового окна, открывающегося при нажатии на кнопку на тулбаре

Всё это либо упаковывают в zip-архив и устанавливают в браузер через страницу Расширения/Дополнения (предварительно сменив расширение архива в зависимости от браузера), либо просто помещают в отдельную папку и опять устанавливают через страницу Расширения/Дополнения, указав путь к этой папке (ручная установка в распакованном виде доступна в хроме).
Возможна программная установка расширений.

Логику расширения пишут на JS, интерфейс пользователя создают с помощью HTML+CSS, а файл настроек пишут на INI/JSON/XML.
Для написания расширений достаточно блокнота.

Исключение — Internet Explorer, расширения для которого пишутся на компилируемом ЯП (C#, C++, Delphi, ..) с помощью COM. Его мы здесь рассматривать не будем.

3. С чего начать?
1. Сначала Chrome.
Во-первых, по нему больше всего инфы в инете.
Во-вторых, он попросту проще.
В-третьих, он не только популярнее сам по себе, но имеет множество клонов, которые, также как и он, основаны на Chromium и обычно совместимы с расширениями Chrome. Это Яндекс.Браузер, Амиго, Интернет от Mail.Ru… Тысячи их.
2. Потом Opera.
Когда вышла Opera 15 (Opera Next) на хромовском движке, оф. документацию по расширениям к старой опере аффффтары просто потёрли, глубоко наплевав на тех, кто привык к старой опере и вообще в гробу видел хром. А таких вот топиков в своё время никто не создал. В результате инфы в инете по опере не Next почти не осталось.
Мы в данном топике будем рассматривать оперу ниже 15, т.е. не Next.
3. Потом Firefox.
Расширения к нему можно создавать двумя способами — как я описал в пункте 2 и каким-то левым способом с помощью специального SDK, причём способы плохо совмещаются друг с другом. Поэтому добрая половина кода в инете, касаемо огнелиса, у вас может просто не заработать. Мы в данном топике будем учиться юзать именно способ без SDK, ибо он проще.
4. Ну и потом IE.
Который по вышеупомянутой причине рассматриваться в топике не будет.

4. Зачем нужен этот топик? Вон статья на хабре, вон оф. документация, вон ещё статья на хабре, что ещё надо?
1) Вам не нужно — вы и не создавайте топиков, и этот не читайте. А некоторым нужно. Мне бы в своё время пригодился.
2) По расширениям к хрому инфа и правда есть, но вот с огнелисом и оперой сложнее. Про оперу — см. выше.
3) Я не прошу отправлять этот топик в важные (хотя буду рад, если он туда попадёт), не буду его апить каждые полчаса. Так что вам он не мешает ничем. Не нужно его трогать.
4) Этот топик нужен мне. Чтобы в свободное время или просто когда лень работать продолжать углублять свои знания и расширять специализацию. И вы бы лучше занялись чем-то таким, нежели ругать и портить чужое.

5. Пишем простое расширение к Chrome.
Расширение будет добавлять всплывающий баннер с текстом «Ура!» на каждую страницу в браузере, в левый-верхний угол.
Итак, приступим.
1. Создаём отдельную папку. Называем её, например, HelloChromeAPI.
2. В папке создаём файл настроек расширения.
Для этого запускаем блокнот, пишем такой код

JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
    "name": "Hello Chrome API",
    "version": "1.0.0.0",
 
    "manifest_version": 2,
 
    "content_scripts": [
        {
            "matches": [ "http://*/*" ],
            "js": [ "content_scripts/end.js" ],
            "run_at": "document_end"
        }
    ]
}

сохраняем в нашу папку под именем manifest.json обязательно в кодировке UTF-8.
Такой формат представления данных называется JSON: записи через запятую, имена их в кавычках, каждая запись может включать в себя подзаписи разного типа (тогда заключается в {}) или подзаписи одного и того же типа (тогда называется массивом и заключается в []).
«name» — имя расширения, которое будет видно в браузере
«version» — версия расширения, которую тоже будет видно в браузере
«manifest_version», равное 2, указывает, что расширение будет совместимо с Chrome выше версии 13. Если вместо 2 указать 1, то расширение будет работать в хромах с 5 по

12. Такие хромы сейчас редки, но если уж писать серьёзный проект, то нужно сделать дубликаты расширения для обоих поколений хрома.
«content_scripts» — это массив контентных скриптов (то есть скриптов, которые, как говорилось выше, выполняются на каждой странице в браузере). В данном случае в списке один элемент (он ограничен скобками {}), так как скрипт в нашем расширении будет только 1
«matches» — маска, указывающая, на каких страницах будет выполняться скрипт. * означает любую последовательность символов. Наш скрипт будет выполнять на всех страницах протокола http. Почему только http? Потому что на страницах протокола https браузер проверяет, нет ли левого html-кода, и если есть, то ругается. Может ругаться и антивирус.
«run_at» указывает, на каком этапе загрузки страницы скрипт будет выполняться.
Значение «document_start» = не загружено ничего (скрипт сможет вывести окошко alert или изменить window.location.href, но не сможет работать с document.body, его просто не будет)
Значение «document_end» = загружен код страницы, доступны document и document.body, но могут выполнять какие-то скрипты, которые ещё просто не успели выполниться
Значение «document_idle» = простой, когда никакой другой скрипт не выполняется. точнее ближайший период простоя с начала загрузки. Рекомендуется использовать для сложных ресурсоёмких скриптов, когда в то же время не требуется, чтобы скрипт выполнился прямо сразу после загрузки.
«js» — это массив путей к скриптам, выполняющимся с данными matches. Скрипт один, он будет называться end.js (это будет означать, что скрипт выполняется по document_end) и лежать в подпапке content_scripts в нашей папке расширения.
3. Создадим подпапку content_scripts, а в ней скрипт end.js.
В нём пропишем код

Javascript
1
document.body.innerHTML += '<div style="background-color: #FFFFFF; border-color: black; border-style: solid; color: black; font-family: 'Segoe UI'; font-size: 24px; position: fixed; left: 0; top: 0; width: 300px; height: 110px; padding: 8px; z-index: 1000000000">Ура!</div>';

Этот скрипт добавляет код баннера в код body той страницы, на которой выполняется.
Он тоже обязательно должен быть сохранён в UTF-8.
Примечание: стилей так много затем, чтобы перекрыть стили, применённые на сайте, куда вставляется баннер, и баннер на всех сайтах выглядел одинаково.
4. Установим расширение в браузер.
Откроем меню браузера кнопкой справа-вверху, затем пункт Инструменты, затем выберем пункт Расширения.
Или просто пройдём по ссылке chrome://extensions
Поставим флажок Режим разработчика, нажмем Загрузить распакованное расширение и укажем нашу папку.
5. Теперь зайдём на любую страницу, использующую http (см. выше)
mail.ru подойдёт, наш форум подойдёт, а google.ru нет.
Когда страница загрузится до конца, слева-вверху появится обещанный баннер.
6. На странице расширений мы можем отключить или удалить расширение с помощью соотв. кнопок.
А если мы решим изменить код manifest.json, end.js и т.д., то чтобы изменения применились, мы должны воспользоваться кнопкой Обновить.

6. Добавляем кнопочку на панель инструментов Chrome
Теперь оно будет ещё добавлять кнопочку на панель инструментов хрома и при нажатии на кнопку открывать диалоговое окно.
Нам понадобится Chrome 20 или новее.
1. Откроем наш файл manifest.json
Изменим его таким образом

JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
    "name": "Hello Chrome API",
    "version": "1.0.0.0",
 
    "manifest_version": 2,
 
    "content_scripts": [
        {
            "matches": [ "http://*/*" ],
            "js": [ "content_scripts/end.js" ],
            "run_at": "document_end"
        }
    ], // <-- здесь добавим запятую
 
    "browser_action": {
          "default_icon": {                       // иконки для кнопки 
            "19": "/images/br-icon19.png",         // иконка 19x19 пкс
            "38": "/images/br-icon38.png"          // иконка 38x38 пкс
          },
          "default_title": "Наша кнопка",         // всплывающая подсказка появл при наведении мыши на кнопку
          "default_popup": "popup/popup.html"     // страница появл во всплывающем окне откр при нажатии на кнопку
    }
}

и сохраним.
2. Создадим подпапки images и popup.
В images создадим две картинки, как указано в манифесте. Картинки могут быть одинаковыми, отображается только одна, в зависимости от расширения монитора (на больших мониторах — 38×38, на маленьких — 19×19).
В popup создадим файл popup.html и запишем в него такой код

HTML5
1
<div style="width: 300px; height: 100px">Ура!</div>

Сохранять тоже лучше в UTF-8.
3. Чтобы изменения вступили в силу, зайдём в Расширения и нажмём кнопку Обновить под нашим расширением.
4. На панели инструментов (слева вверху) появится кнопка с заданной иконкой.
Нажав на кнопку, увидим окошко, в котором будет надпись «Ура!».
Заметим, что размер окошка соответствует размеру, указанному в style корневого элемента popup.html (т.е. 300 x 100 пкс).
Если этот размер не указывать, окошко будет таким, чтобы в него поместилось содержимое этого элемента, т.е. надпись «Ура!».

7. Добавляем иконку в расширение к Chrome.
1. Подправим манифест.

JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
    "manifest_version": 2,
 
    "name": "Hello Chrome API",
    "version": "1.0.0.0",
 
    "manifest_version": 2,
 
    // добавим такие строчки
    "icons": {
        "48": "/images/icon48.png"
    },
 
    "content_scripts": [
        {
            "matches": [ "http://*/*" ],
            "js": [ "content_scripts/end.js" ],
            "run_at": "document_end"
        }
    ],
 
    "browser_action": {
          "default_icon": {
            "19": "/images/br-icon19.png",
            "38": "/images/br-icon38.png"
          },
          "default_title": "Наша кнопка",
          "default_popup": "popup/popup.html"
    }
}

2. Создадим файл icon48.png в папке images.
Это будет иконка расширения, отображаемая на странице расширений вместо дефолтного значка с фрагментом мозаики.
Примечание: помимо иконки размером в 48 пкс, мы можем сделать иконки размером 16, 32, 128 пкс, дописав внутрь ветви «icons:» соотв. строчки, не забывая про запятые. Но это нужно только если мы решим нарисовать действительно разные иконки для расширения. Просто растянув иконку под 16, 32, 128 пкс, мы ничего не достигнем — хром это сделает и сам, где оно понадобится. Я ограничился 48 пкс.
3. Обновим расширение на странице расширений.

8. Пишем простое расширение к Opera.
Расширение будет работать аналогично расширению к хрому.
Опера у меня версии 12. На других не пробовал. На 15 и выше, ещё раз, работать не должно.
Все файлы, как и с хромом, лучше сохранять в UTF-8, иначе кириллица будет отображаться неверно.
Приступим.
1. Создаём отдельную папку. Называем её, например, HelloOperaAPI.
2. В папке создаём файл настроек расширения.
Для этого запускаем блокнот, пишем такой код

XML
1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<widget xmlns="http://www.w3.org/ns/widgets">
    <name>Hello Opera API</name>
    <version>1.0.0.0</version>
</widget>

сохраняем в нашу папку под именем config.xml обязательно в кодировке UTF-8.
Такой формат представления данных называется XML.
<name> — имя расширения, которое будет видно в браузере
<version> — версия расширения, которую тоже будет видно в браузере
А вот никакого упоминания о скриптах в файле не будет. Мы просто создадим в папке с расширением подпапку include и файл .js с любым именем. Этого будет достаточно, чтобы скрипт работал.
3. Сказано — сделано. Создаём подпапку include и файл script_end.js

Javascript
1
2
3
4
5
6
7
// ==UserScript==
// @include [url]http://*/*[/url]
// ==/UserScript==
 
document.addEventListener("DOMContentLoaded", function() {
    document.body.innerHTML += '<div style="background-color: #FFFFFF; border-color: black; border-style: solid; color: black; font-family: 'Segoe UI'; font-size: 24px; position: fixed; left: 0; top: 0; width: 300px; height: 110px; padding: 8px; z-index: 1000000000">Ура!</div>';
}, false);

DOMContentLoaded позволяет создать эффект «document_end» из хрома (а сам юзерскрипт в опере выполняется фактически по «document_start».
«==UserScript==…==/UserScript==» вверху — это не просто комментарий. Там указываются настройки данного скрипта. @include здесь работает по принципу «matches» из Chrome.
Скрипт тоже обязательно должен быть сохранён в UTF-8.
Этот скрипт добавляет код баннера в код body той страницы, на которой выполняется.
4. В папке с расширением создадим index.html. Это фоновая страница. Мы можем оставить её пустой, потому что фоновых скриптов пока нет и она нам не нужна, но в расширении для оперы она должна быть обязательно.
5. Упакуем расширение в zip-архив. Сменим его расширение с zip на oex.
6. Запустим оперу, зайдём в меню, выберим пункт Дополнения, перетащим туда oex.
7. Проверим, появляются ли баннеры при загрузке страниц.

9. Добавляем кнопку на панель инструментов Opera
На панели инструментов справа-вверху появится кнопка. При нажатии на неё откроется всплывающее окошко с заданной страницей. Всё как в хроме.
Нам понадобится Chrome 20 или новее.
1. Изменим фоновую страницу index.html таким образом

HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!doctype html>
<html lang="en">
    <head>
        <script>
            window.addEventListener("load", function(){
                var ToolbarUIItemProperties = {
                    title: "Наша кнопка",
                    icon: "/images/br-icon19.png",
                    popup: {
                        href: "popup/popup.html",
                        width: 300,
                        height: 100
                    }
                }
                opera.contexts.toolbar.addItem(opera.contexts.toolbar.createItem(ToolbarUIItemProperties));
            }, false);
        </script>
    </head>
    <body>
    </body>
</html>

и сохраним.
opera.contexts.toolbar — это класс из Opera API.
У хрома тоже есть API — chrome.*.
2. Создадим подпапки images и popup.
В images создадим картинку, как указано в манифесте.
В popup создадим файл popup.html и запишем в него такой код

Примечание:

в отличие от хрома, размер окошка формируется не через style корневого элемента popup.html, а через width и height, прописанные в ToolbarUIItemProperties в index.html.
3. Чтобы изменения вступили в силу, удалим расширение и установим его обратно.
4. На панели инструментов (слева вверху) появится кнопка с заданной иконкой.
Нажав на кнопку, увидим окошко, в котором будет надпись «Ура!».

10. Добавляем иконку в расширение Opera.
1. Путь к иконке указывается в файле настроек — config.xml.
За него отвечает тэг icon с атрибутом src, в котором указывается относительный путь к иконке (относительно основной папке расширения).
Откроем файл config.xml.
Внутри <widget> добавим строчку

XML
1
<icon src="/images/icon48.png" />

Должно получиться так:

XML
1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<widget xmlns="http://www.w3.org/ns/widgets">
    <name>Hello Opera API</name>
    <version>1.0.0.0</version>
    <icon src="/images/icon48.png" />
</widget>

2. Создадим файл icon48.png (это может любая картинка 48×48 пкс) в папке images.
3. Перепакуем и переустановим расширение.
В списке расширений вместо дефолтного значка (сиреневого фрагмента мозаики) будет отображаться наша иконка.


11. Пишем простое расширение Firefox.
12. Добавляем панель инструментов в Firefox.
13. Добавляем иконку в расширение Firefox.

Продолжение следует…
Пока что читаем здесь и экспериментируем.

Пара советов и ссылок на закуску
Конечно, информации из этого топика ещё недостаточно, чтобы написать любое расширение к браузеру Chrome, Firefox или Opera.
Но общее представление о расширениях и особенностях их разработки вы получили.
Поэтому, воспользовавшись данными ссылками в связке с гуглом, Вы при желании сможете углубиться в расширения и писать крупные, уникальные продукты.
Здесь же обещанные магазины расширений.
CHROME
https://developer.chrome.com/extensions/ — официальная документации.
Вот описание в ней контентных скриптов и кнопки browser-action:
https://developer.chrome.com/e… nt_scripts
https://developer.chrome.com/e… wserAction
https://chrome.google.com/webs… ions?hl=ru — магазин.
OPERA
https://addons.opera.com/ru/extensions/ — магазин.
FIREFOX
https://addons.mozilla.org/ru/firefox/ — магазин.

Добавлено через 2 минуты
В процессе написания топика нашёл на форуме баг.
В кодах JSON, XML и др. текст «https://www.cyberforum.ru/images/картинка.png» заменяется на «https://www.cyberforum.ru/images/картинка.png».
То есть если написать

JSON
1
2
3
{
    "48": "ЗДЕСЬ_НЕТ_НИЧЕГОimages/icon48.png"
}

то получится

JSON
1
2
3
{
    "48": "https://www.cyberforum.ru/images/icon48.png"
}

Правда, написав

JSON
1
2
3
{
    "48": "/images/icon48.png"
}

мы получим то, что ожидали. Но иной код от этого может сделаться также нечитабельным, как и из-за добавления «https://www.cyberforum.ru/».



2



Понравилась статья? Поделить с друзьями:
  • Как написать скрипт для 3d max
  • Как написать скрипт вирус
  • Как написать скрипт автокликер
  • Как написать скрипт tampermonkey
  • Как написать скрипт sql