Добрый день, Хабр.
Я хочу написать цикл статей о создании расширений для Google Chrome. К этому меня побуждает, во-первых, практическая польза самого процесса разработки и последующего использования: вы сами определяете, какие ещё задачи хотите решить не выходя из браузера и, во-вторых, отсутствие каких-либо внятных гайдов, туториалов и справочников на русском языке, за исключением, пожалуй, этой и вот этой статей на Хабре. Основная цель цикла — систематизировать разрозненную информацию и облегчить поиск потенциальным разработчикам, благо индексируется Хабр хорошо :)
В первой (этой, то бишь) статье, на примере простейшего расширения, будут рассмотрены все основные моменты, связанные с разработкой, отладкой и использованием расширения, конфигурационный файл manifest.json и начала chrome.* API. Первая же статья, думаю, будет не очень полезна опытным разработчикам (это дисклеймер).
Лучшая теория — практика, а поэтому, не откладывая в долгий ящик, создаём папку hello_world, а в ней текстовый документ manifest.json и печатаем туда следующий код:
Это — программа минимум. Если зайти в «Гаечный ключ → Инструменты → Расширения», установить галку «Режим разработчика», нажать кнопку «Загрузить распакованное расширение...» и указать нашу папку «Hello world», то расширение появится в списке установленных, но делать, естественно, оно пока ничего не будет, потому как не умеет.

А раз не умеет — надо научить. Отредактируем manifest.json:
И создадим файл background.html в котором будет написан сценарий, выполняемый в фоновом режиме. Например, такой:
Сценарий в background.html будет выполнен один раз, при старте браузера и расширения, то есть, при открытии ещё одной вкладки или окна, повторное исполнение сценария не произойдёт. В нашем случае он каждые 10 секунд будет писать в консоль сакраментальную фразу, и это, кстати, надо бы проверить.
Для отладки удобно использовать служебную страницу chrome://extensions/ со включённым режимом разработчика.

В принципе, она дублирует функционал страницы управления расширениями из «Гаечного ключа», но мне, субъективно, нравится больше. Как-то компактнее, что ли?
Здесь нас интересуют две позиции: строка «ID» с внутренним идентификатором расширения и подраздел «Проверить активные режимы просмотра» в котором мы видим созданный нами background.html и, щёлкнув по нему, можем проконтролировать исполняемый сценарий.
Смотрим и убеждаемся, что сценарий исправно пишет в консоль хэллоуворлды:

Обратите внимание на заголовок формата chrome-extension://ID/filename. Зная идентификатор расширения таким образом можно добраться до любого его файла. Опять же удобно в процессе отладки расширения.
Пока наше расширение представляет эдакую вещь в себе, исполняя в фоне некий сценарий. Для того, чтобы оно начало взаимодействовать с браузером и его компонентами нужно познакомиться с chrome.* API. Так, например, для взаимодействия с окном браузера используются методы chrome.browserAction, а значения по умолчанию задаются в manifest.json следующим образом:
Не забываем создать popup.html (пока оставим его пустым) и положить иконку в папку img, щёлкаем на «Перезагрузить» на странице chrome://extensions/ и смотрим на результат. Иконка нашего расширения появилась на панели расширений, а при клике на неё возникает пустое всплывающее окошко.

Иконка для тех, кто проходит по шагам:

Всё это управлябельно с помощью методов chrome.browserAction из сценариев:
Для практике давайте заставим background.html сделать что-нибудь полезное, вместо того, чтобы просто гадить в консоль. Вот, хотя бы часы. Поверх иконки будет отображаться количество минут, при наведении — время в формате ЧЧ: ММ: СС, а во всплывающем окошке — часы со стрелками.
background.html
popup.html
Сохраняем, перезапускаем, проверяем — красота!

Собственно, мы сделали простое расширение (а заодно и canvas припомнили). Для Getting Started, во всяком случае, достаточно. Осталось только привести его к годному для распространения виду — упаковать. Для этого на той же странице chrome://extensions/ давим на «Упаковка расширений...», указываем корневой каталог (тот, где лежит manifest.json), давим «Ок» и получаем файл формата *.crx на выходе. Это и есть наше упакованное расширение. Открыв его с помощью Хрома, мы установим расширение.
Упакованный пример из статьи для установки
Архив с исходниками
В следующей статье цикла я планирую подробно разобрать chrome.* API, а в дальнейшем — взаимодействие с различными сайтами и использование локальных хранилищ данных. Если вы считаете, что я что-то упустил в азах или у вас есть пожелания по поводу следующих статей цикла — прошу изложить их в комментариях.
See ya!
Я хочу написать цикл статей о создании расширений для Google Chrome. К этому меня побуждает, во-первых, практическая польза самого процесса разработки и последующего использования: вы сами определяете, какие ещё задачи хотите решить не выходя из браузера и, во-вторых, отсутствие каких-либо внятных гайдов, туториалов и справочников на русском языке, за исключением, пожалуй, этой и вот этой статей на Хабре. Основная цель цикла — систематизировать разрозненную информацию и облегчить поиск потенциальным разработчикам, благо индексируется Хабр хорошо :)
В первой (этой, то бишь) статье, на примере простейшего расширения, будут рассмотрены все основные моменты, связанные с разработкой, отладкой и использованием расширения, конфигурационный файл manifest.json и начала chrome.* API. Первая же статья, думаю, будет не очень полезна опытным разработчикам (это дисклеймер).
Hello world
Лучшая теория — практика, а поэтому, не откладывая в долгий ящик, создаём папку hello_world, а в ней текстовый документ manifest.json и печатаем туда следующий код:
{ "name" : "Hello world", //Название расширения "version" : "1.0", //Версия "description" : "This is a simple chrome extention" //Краткое описание }
Это — программа минимум. Если зайти в «Гаечный ключ → Инструменты → Расширения», установить галку «Режим разработчика», нажать кнопку «Загрузить распакованное расширение...» и указать нашу папку «Hello world», то расширение появится в списке установленных, но делать, естественно, оно пока ничего не будет, потому как не умеет.

Учим и отлаживаем
А раз не умеет — надо научить. Отредактируем manifest.json:
{ "name" : "Hello world", "version" : "1.0", "description" : "This is a simple chrome extention", "background_page" : "background.html" }
И создадим файл background.html в котором будет написан сценарий, выполняемый в фоновом режиме. Например, такой:
<script type="application/javascript"> window.onload = function() { window.setInterval( function() { console.log("Hello world"); }, 10000); } </script>
Сценарий в background.html будет выполнен один раз, при старте браузера и расширения, то есть, при открытии ещё одной вкладки или окна, повторное исполнение сценария не произойдёт. В нашем случае он каждые 10 секунд будет писать в консоль сакраментальную фразу, и это, кстати, надо бы проверить.
Для отладки удобно использовать служебную страницу chrome://extensions/ со включённым режимом разработчика.

В принципе, она дублирует функционал страницы управления расширениями из «Гаечного ключа», но мне, субъективно, нравится больше. Как-то компактнее, что ли?
Здесь нас интересуют две позиции: строка «ID» с внутренним идентификатором расширения и подраздел «Проверить активные режимы просмотра» в котором мы видим созданный нами background.html и, щёлкнув по нему, можем проконтролировать исполняемый сценарий.
Смотрим и убеждаемся, что сценарий исправно пишет в консоль хэллоуворлды:

Обратите внимание на заголовок формата chrome-extension://ID/filename. Зная идентификатор расширения таким образом можно добраться до любого его файла. Опять же удобно в процессе отладки расширения.
Взаимодействие с браузером
Пока наше расширение представляет эдакую вещь в себе, исполняя в фоне некий сценарий. Для того, чтобы оно начало взаимодействовать с браузером и его компонентами нужно познакомиться с chrome.* API. Так, например, для взаимодействия с окном браузера используются методы chrome.browserAction, а значения по умолчанию задаются в manifest.json следующим образом:
{ "name" : "Hello world", "version" : "1.0", "description" : "This is a simple chrome extention", "background_page" : "background.html", "browser_action" : { "default_title" : "Hello world!!!", //Текст, всплывающий при наведении курсора на иконку (если не задан, то всплывает название расширения) "default_icon" : "img/icon_world.png", //Иконка для панели расширений (по умолчанию) "default_popup" : "popup.html" //Всплывающее окно при клике на иконке } }
Не забываем создать popup.html (пока оставим его пустым) и положить иконку в папку img, щёлкаем на «Перезагрузить» на странице chrome://extensions/ и смотрим на результат. Иконка нашего расширения появилась на панели расширений, а при клике на неё возникает пустое всплывающее окошко.

Иконка для тех, кто проходит по шагам:
Всё это управлябельно с помощью методов chrome.browserAction из сценариев:
<script type="text/javascript"> chrome.browserAction.setTitle({title:"New title"}); //Устанавливает новый всплывающий при наведении на иконку текст chrome.browserAction.setPopup({popup:"new_popup.html"}); //Устанавливает новое всплывающее окно при клике на иконке chrome.browserAction.setIcon({path:"new_icon.png"}); //Устанавливает новую иконку chrome.browserAction.setBadgeText({text:"text"}); //Устанавливает текст поверх иконки chrome.browserAction.setBadgeBackgroundColor({color:[0,0,0]}); //Устанавливает фон текста поверх иконки </script>
Для практике давайте заставим background.html сделать что-нибудь полезное, вместо того, чтобы просто гадить в консоль. Вот, хотя бы часы. Поверх иконки будет отображаться количество минут, при наведении — время в формате ЧЧ: ММ: СС, а во всплывающем окошке — часы со стрелками.
background.html
<script type="application/javascript"> window.onload = function() { window.setInterval( function() { var now = new Date(); var h = now.getHours(); var m = now.getMinutes(); var s = now.getSeconds(); var badge_text = (m < 10 ? "0" + m : m).toString(); var title_text = (h < 10 ? "0" + h : h) + ":" + (m < 10 ? "0" + m : m) + ":" + (s < 10 ? "0" + s : s); chrome.browserAction.setBadgeText({text: badge_text}); chrome.browserAction.setTitle({title: title_text}); }, 1000); } </script>
popup.html
<!DOCTYPE html> <html> <head> <style type="text/css"> * { margin: 0; padding: 0; border: 0; } body { background: #000; } </style> <script type="text/javascript"> Clock = function() { this.canvas = false; this.pi = Math.PI; } Clock.prototype = { get_time: function() { var now = new Date(); var result = { milliseconds: now.getMilliseconds(), seconds: now.getSeconds(), minutes: now.getMinutes(), hours: now.getHours() } return result; }, init: function() { this.canvas = document.getElementById("clock").getContext("2d"); }, draw: function() { var now = this.get_time(); var hangle = (this.pi/6)*now.hours + (this.pi/360)*now.minutes + (this.pi/21600)*now.seconds + (this.pi/21600000)*now.milliseconds; var mangle = (this.pi/30)*now.minutes + (this.pi/1800)*now.seconds + (this.pi/1800000)*now.milliseconds; var sangle = (this.pi/30)*now.seconds + (this.pi/30000)*now.milliseconds; this.canvas.save(); this.canvas.fillStyle = "#000"; this.canvas.strokeStyle = "#000"; this.canvas.clearRect(0,0,200,200); this.canvas.fillRect(0,0,200,200); this.canvas.translate(100,100); this.canvas.rotate(-this.pi/2); this.canvas.save(); this.canvas.rotate(hangle); this.canvas.lineWidth = 8; this.canvas.strokeStyle = "#ffffff"; this.canvas.fillStyle = "#ffffff"; this.canvas.lineCap = "round"; this.canvas.beginPath(); this.canvas.moveTo(-10,0); this.canvas.lineTo(50,0); this.canvas.stroke(); this.canvas.restore(); this.canvas.save(); this.canvas.rotate(mangle); this.canvas.lineWidth = 4; this.canvas.strokeStyle = "#ffffff"; this.canvas.lineCap = "square"; this.canvas.beginPath(); this.canvas.moveTo(-20,0); this.canvas.lineTo(75,0); this.canvas.stroke(); this.canvas.restore(); this.canvas.save(); this.canvas.lineWidth = 2; this.canvas.strokeStyle = "#ffffff"; this.canvas.fillStyle = "#333"; this.canvas.beginPath(); this.canvas.arc(0,0,8,0,this.pi*2,true); this.canvas.fill(); this.canvas.stroke(); this.canvas.restore(); this.canvas.save(); this.canvas.rotate(sangle); this.canvas.lineWidth = 2; this.canvas.strokeStyle = "#ff0000"; this.canvas.lineCap = "square"; this.canvas.beginPath(); this.canvas.moveTo(-30,0); this.canvas.lineTo(85,0); this.canvas.stroke(); this.canvas.restore(); this.canvas.save(); this.canvas.lineWidth = 6; this.canvas.fillStyle = "#ff0000"; this.canvas.beginPath(); this.canvas.arc(0,0,3,0,this.pi*2,true); this.canvas.fill(); this.canvas.restore(); this.canvas.save(); this.canvas.lineWidth = 6; this.canvas.strokeStyle = "#ffffff"; this.canvas.beginPath(); this.canvas.arc(0,0,95,0,this.pi*2,true); this.canvas.stroke(); this.canvas.restore(); this.canvas.restore(); } } window.onload = function() { var clock = new Clock(); clock.init(); window.setInterval(function() { clock.draw(); }, 10); } </script> </head> <body> <canvas id="clock" width="200" height="200"></canvas> </body> </html>
Сохраняем, перезапускаем, проверяем — красота!

Собственно, мы сделали простое расширение (а заодно и canvas припомнили). Для Getting Started, во всяком случае, достаточно. Осталось только привести его к годному для распространения виду — упаковать. Для этого на той же странице chrome://extensions/ давим на «Упаковка расширений...», указываем корневой каталог (тот, где лежит manifest.json), давим «Ок» и получаем файл формата *.crx на выходе. Это и есть наше упакованное расширение. Открыв его с помощью Хрома, мы установим расширение.
Упакованный пример из статьи для установки
Архив с исходниками
В следующей статье цикла я планирую подробно разобрать chrome.* API, а в дальнейшем — взаимодействие с различными сайтами и использование локальных хранилищ данных. Если вы считаете, что я что-то упустил в азах или у вас есть пожелания по поводу следующих статей цикла — прошу изложить их в комментариях.
See ya!
