Создаём своё расширение для Google Chrome

На хабре уже есть несколько статей о создании расширений для хрома, поделюсь своим опытом, затронув основные вещи и места, в которых у меня возникли трудности.
Что понадобится для создания расширения в двух словах:
1) Базовые знания Javascript
2) Базовые знания HTML
3) 5$

Покажу создание расширения для хрома на примере своего, созданного для подсчета «потерянного времени» в интернете. То есть это расширение считает время, проведенное на сайтах с возможностью определения посещенных сайтов по категориям: полезное время, либо потерянное время.

Итак, я начинаю создание расширения с создания папки самого расширения, в которую будем класть все создаваемые нами файлы. Назову её «losttime». Далее, я создаю файл manifest.json, выглядит он следующим образом:

manifest.json
 {
  "manifest_version": 2,
  "name": "Lost Time",
  "version": "1.0",
  
  "icons": {
    "128": ""
  },
  "content_scripts": [
    {
      "matches": [ "*://*/*" ],
      "js": [ "content.js" ]
    }
  ],
  "background": {
    "scripts": ["background.js"]
  },
  "permissions": [
    "http://losttime.su/*"
  ],

      "browser_action": {
        "default_title": "LostTime",
        "default_icon": "",
        "default_popup": "popup.html"
    }
	
}


Некоторые из строк должны быть интуитивно понятны, но что обязательно нужно знать:
— Значение manifest_version должно быть обязательно «2»;
— В content_scripts пишем, какой скрипт будет запускаться на всех страницах отдельно;
— В background пишем общий скрипт(фоновый скрипт), который запускается при запуске браузера;
— В permissions пишем адрес сайта, с которого будет браться информация.

Все, что буду использовать я, не обязательно использовать Вам, если вам это по логике просто не нужно. Подробнее о манифесте.

То самое окошко, которое Вы можете видеть по клику на иконку расширения — это страница: popup.html.



Она у меня выглядит следующим образом:

popup.html
<!doctype html>
<html>
  <head>
    <title>Потерянное время LostTime</title>
	    <script src="jquery.js" type="text/javascript"></script> <!-- Подключаю jquery -->
	<link href="css.css" rel="stylesheet" type="text/css"/><!-- Подключаю стили-->
  </head>
  <body>
  <div id="options"><!-- меню -->
<a  href="/popup.html"><img class='img' src="" Title = "Короткая статистика за сегодня"></a>
<a href="/options.html"><img class='img' src="images/options.png" Title="Настройки программы"></a>
<a href="/stat.html"><img class='img' src="images/stat.png" Title="Подробная статистика о посещаемости"></a>
</div>
<div id="dannie"></div> <!-- в этот блок буду загружать данные, которые будут показываться пользователю-->
    <script src="popup.js"></script><!-- скрипт, выполняющийся при нажатии на иконку расширения-->
  </body>
</html>


Чтобы было понятнее, описание кода вставил в самом HTML. Меню я организую просто: на картинку ставлю внутреннюю ссылку расширения.

Раз уж начал про popup.html, то расскажу сразу и о popup.js

Выглядит он у меня весьма просто:

popup.js
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://losttime.su/?tmpl=login&token="+localStorage['lostlogin'], true); // тут происходит ГЕТ запрос на указанную страницу
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) // если всё прошло хорошо, выполняем, что в скобках
  {
	var dannie = document.getElementById('dannie');
	dannie.innerHTML = xhr.responseText; // добавляем в блок с id=dannie  полученный код
  }
}
xhr.send();


Описание кода также вставил.

Именно описанная выше конструкция позволяет вытащить и вывести содержание с Вашего, а может и не с Вашего сайта. Но, что важно знать:
— В файле манифеста обязательно в поле permissions пишем адрес сайта, с которого будет браться информация.
— Файл popup.js связан с фоновым скриптом background.js, т.к. данные, занесенные в локальное хранилище на background.js, видны и на popup.js.

Перед тем, как рассмотреть файл фонового скрипта background.js, рассмотрим файл скрипта, который запускается на каждой странице отдельно: content.js

У меня он выглядит так:

content.js
function onBlur() { // окно теряет фокус
	chrome.runtime.sendMessage({site:sait,time:localStorage[sait]}); // отправка сообщения на background.js
	localStorage[sait] = '0';	
}
    window.onblur = onBlur; // если окно теряет фокус
	function sec() //выполняется каждую секунду
	{ 
	  if(document.webkitVisibilityState == 'visible')//если страница активна
	    {
		   localStorage[sait] =  parseInt(localStorage[sait],10) +1; // обновляем данные о сайте в локальном хранилище
	    }
	}			 	
var sait=location.hostname; // на каком сайте находится скрипт
localStorage[sait] = '0';
	setInterval(sec, 1000);// запускать функцию каждую секунду



Наиболее интересный момент из моего скрипта, я считаю, должен быть:
chrome.runtime.sendMessage({site:sait,time:localStorage[sait]});
Тут происходит отправка сообщения background скрипту, а именно две переменные: site:sait — содержит адрес сайта, на котором скрипт
time:localStorage[sait] — количество времени, проведенное на этом скрипте.

Далее, рассмотрим фоновый скрипт background.js, где и происходит приём данных, а точнее рассмотрим саму функцию приёма данных.

background.js
chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
var a = request.site; // данные о сайте
var b = request.time; // данные о проведенном времени
// тут делаем с этими данными что хотим.
  });


Вот, собственно, и она. Разбирать подробно ничего не стану, т.к. это в принципе и не нужно. Достаточно знать наглядный пример, чтобы осуществить задуманное. Если в скрипте background.js добавить какие-либо данные в локальное хранилище( а также куки, web sql), то эти же данные можно будет использовать и в popup.js скрипте.

Вот собственно всё, что я хотел поведать о создании расширения, но я затрону еще один момент, в котором у меня возникли трудности.

На странице настроек мне необходимо было организовать перетаскивание сайтов в разные колонки.



Т.к. данные вставляются посредством InnerHtml, то данная возможность просто так не появится. Вот, что пришлось организовать:

$('#dannie').on('mouseover', '.sait', function( ) {
	$(this).css({'border':'3px solid #ffffff'});
});
$('#dannie').on('mouseout', '.sait', function( ) {
	$(this).css({'border':'3px solid black'});
});
$('#dannie').on('mousedown', '.sait', function( ) {
	$(this).css({'border':'3px solid black'});
});

$('#dannie').on('mouseover', '.sait', function( ) {
	  $('.sait').draggable({ 
helper:'clone'
	});  
});

вместо привычного:
$('.sait').mouseover(function(){
$('#'+this.id).css({'border':'3px solid #ffffff'});
});
$('.sait').mouseout(function(){
$('#'+this.id).css({'border':'3px solid black'});
});
$('.sait').mousedown(function(){
$('#'+this.id).css({'border':'0px solid black'});
});
	$('.sait').draggable(
	{ 
helper:'clone',
	});


Думаю, объяснять не нужно. Почитать подробнее можете по ссылке

Тестирование расширения

Заходим в Настройки — Инструменты — Расширения, жмем на «Загрузить распакованное расширение»

Публикация расширения
Заходим на страницу оплачиваем 5$, публикуем.
Я не останавливаюсь на моментах, с которыми у меня не возникли трудности. А трудности возникли при оплате карточкой:
— В моём случае должен быть подключен 3д пароль.
Если Вам при оплате пишет ошибку, звоните своему банку и узнавайте. Мне за минуту помогли и всё гуд.

Источники:
Документация
Об отправке сообщений
О манифесте
Форум javascript

А также само расиширние.

Спасибо за прочтение. Всем удачи.
Поделиться публикацией

Комментарии 15

    0
    Расширение возможно кому-то окажется полезным, но вот код нельзя назвать хорошим.
    1. Вместо
      <a href="/options.html"><img class='img' src="images/options.png" Title="Настройки программы"></a>
      

      Нужно делать
      var optionsUrl = chrome.extension.getURL('options.html');
      chrome.tabs.create({url: optionsUrl});
      

      а еще лучше
      var optionsUrl = chrome.extension.getURL('options.html');
      chrome.tabs.query({url: optionsUrl}, function(tabs) {
        if (tabs.length)
          chrome.tabs.update(tabs[0].id, {active: true, url: optionsUrl});
        else
          chrome.tabs.create({url: optionsUrl});
      });
      

    2. Если у вас в popup используется jQuery, почему вы делаете new XMLHttpRequest(), а не используете $.ajax?
    3. Таймер на всех страницах в браузере, который обновляет данные раз в секунду для каждой страницы? Что-то мне это совсем не нравится. Если уж хотите крутить таймер, так запускайте таймер только в активной вкладке, а в неактивных останавливайте его.
    4. А CSS вам чем не угодил?
      $('#dannie').on('mouseover', '.sait', function( ) {
          $(this).css({'border':'3px solid #ffffff'});
      });
      $('#dannie').on('mouseout', '.sait', function( ) {
          $(this).css({'border':'3px solid black'});
      });
      $('#dannie').on('mousedown', '.sait', function( ) {
          $(this).css({'border':'3px solid black'});
      });
      

    5. Получение данных с losttime.su/?tmpl=login&token=… есть, а отправку данных вы не описали. Это не так важно или ее вообще нет?
      0
      1) я не профи в этой области, делал как знаю и как понятнее для меня.

      Остальные ответы исходя из первого.

      3) Согласен, но это ничтожная нагрузка. Возможно, это я исправлю.
      4) В моём случае, я могу добавлять разные анимации к тому же.
      5) Отправляются данные ГЕТ запросом. Посчитал, что это будет лишней информацией.
        0
        Вы только не принимайте близко к сердцу

        1. Если вы не профи, не пишите туториалов.
        2. Прислушивайтесь к тому, что вам советуют.
        3. Нагрузка не ничтожная. Откройте 50 вкладок и диспетчер процессов хрома и увидите.
        4. Можете, но это Чушь Петровна. Если надо будет добавить анимации — добавите анимации, а стили должны быть в CSS.
        5. А в ограничение на длину GET запроса вы не упираетесь? Или вы GET с телом шлёте?
          0
          3) я думаю тут дело не в нагрузке, а в корректности данных. Если у меня закрыта вкладка — зачем ее считать?

          Я использую расширение Time Tracker, считает только активную.
        –1
        Статья рассчитана на новичков, знающих лишь основы javascript и html. Для них я и писал. При начинании написания расширения, я бы больше хотел увидеть такой пост, чем статью с заумными непонятными функциями, типа
        var optionsUrl = chrome.extension.getURL('options.html');
        chrome.tabs.query({url: optionsUrl}, function(tabs) {
          if (tabs.length)
            chrome.tabs.update(tabs[0].id, {active: true, url: optionsUrl});
          else
            chrome.tabs.create({url: optionsUrl});
        });
        

        вместо обычного, всем понятного:
        <a href="/options.html"><img class='img' src="images/options.png" Title="Настройки программы"></a>
        
          +1
          Вы не ваяете хоумпэйдж на коленке, вы делаете расширение для браузера. Еще и распространяете его. Еще и пишете статью с ярлычком «tutorial».
          Будьте готовы к конструктивной критике.

          Это не «заумные и непонятные функции», это функции, которые описаны в официальной документации по разработке расширений. А вы сделали «лишь бы работало».
          0
          А вы сделали «лишь бы работало».

          Не согласен. Версия расширения уже не первая, дорабатывал многие моменты. Но и не последняя. Буду также дорабатывать и улучшать.
            +2
            Ничего нового. На хабре уже куча похожих туториалов начального уровня. Причём лучше вашего. Да и в целом тема уже чересчур заезжена в интернетах.
              0
              ммм…
              $('#'+this.id)
              это новый способ получения
              $(this)
              ?
                0
                Нет, это одно и то же. Просто скопировал как было раньше и как стало.
                +1
                А еще у вас там XSS и SQL Injection
                  0
                  Возможно навлеку гнев, но все же поясните какой смысл в jQuery когда тут можно вполне обойтись vanilla.js без особых трудностей (меня как и Anonym смутил порыв ajax-запросов замиксованный с остальным на jQuery)?
                  И не стесняйтесь, публикуйте ссылку на репозиторий в Github.
                    0
                    я с vanilla.js не работал, использую, что на данный момент знаю.
                      0
                      Попробуйте, вам понравится. Хотя бы для того, чтобы понимать какая «магия» происходит в jQuery.
                      0
                      Это так тонко, что я улыбаюсь уже 5 минут. :)

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

                    Самое читаемое