Мне всегда хотелось рассказывать людям об интересных возможностях (технологиях), которые сейчас могут быть доступны каждому, но почему-то не доступны каждому. Да, получилось тавтология, но она в полной мере отображает моё внутреннее негодование на эту животрепещущую для меня тему. Как бы там ни было, речь сейчас будет не о том, как говорится. А поговорим мы сегодня о создании расширений для браузера Google Chrome (далее Хром).

Расширение, которое мы будем разрабатывать на протяжении всей статьи можно найти в магазине Google Chrome Web Store, с той лишь разницей, что там присутствует расширенный функционал. Помимо этого, присутствует исходный код на GitHub, опять таки с оговоркой на то, что там всё написано на CoffeeScript, а здесь будет вестись повествование с JavaScript. Кстати, я не поклонник и не сторонник CoffeeScript, но штука довольно интересная и полезная — советую попробовать.

Если вы когда-нибудь рассматривали идею создания расширения для Chrome, Firefox, Maxthon и прочих браузеров, то, наверное, уже успели заметить, что минимум усилий нужно приложить как раз таки для Хрома. Убедиться в этом можно, взглянув на документацию у соответствующих браузеров.

Постановка задачи

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

  • Расширение должно скрывать все комментарии в социальной сети «VK»;
  • Расширение должно иметь возможность отображать комментарии;
  • Расширение должно иметь возможность отображать комментарии на конкретных страницах;

С первого взгляда всё просто и нам по силам. Однако, в рамках статьи мы реализуем лишь первые два пункта.

Предвижу вопросы, содержание которых может быть примерно таким: «Нафига скрывать комментарии, если в этом вся суть социальной сети?!». Что же, справедливый вопрос, заслуживающий развёрнутого ответа:

Так сложились обстоятельства, что последнее время, когда я вижу комментарии в VK, мне хочется дарить горы фейспалмов комментаторам. Я подписан на большое количество различных пабликов, тематических (веб-разработка) и не очень. И как бы это не казалось странным, самым щедрым я становлюсь именно в группах с интересным для меня содержанием, а не котиками (в моём случае с пандами). Такого количества непрофессионального и безобразного потока информации в комментариях я не видел ещё нигде, да ещё и спорить думают. Помимо этого, комментарии в новостной ленте смотрятся не эстетично. В общем, сказано — сделано.

Каркас расширения

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

Первым делом нужно заполнить три обязательных поля:

{
  "manifest_version": 2, // Начиная с Chrome 18 ставим 2, иначе 1 (не поддерживается такое старьё)
  "name": "My Extension", // Название расширения
  "version": "versionString" // Версия расширения
}

Если с названием всё понятно, а с версией манифеста всё ещё проще, то поподробнее нужно остановиться с версией расширения.

Итак, все мы привыкли, что версия чего-либо состоит из трёх чисел, разделённых точками — Мажорное.Минорное.Патч (Имеется в виду число). С npm, bower и прочими прелестями разговор короткий: либо так, либо никак. А вот Google предлагает следующие варианты:

"version": "1"
"version": "1.0"
"version": "2.10.2"
"version": "3.1.2.4567"

Тем не менее, я сторонник семантического версионирования и советую вам так же придерживаться этой идеи, но не настаиваю.

Далее рекомендуемыми полями идут:

"default_locale": "en", // Язык по умолчанию
"description": "A plain text description", // Описание приложения
"icons": {...}, // Иконки 16 & 48 & 128, дополнительно можно 32 & 64 & 96 & 256 и т.д.

"short_name": "Short Name", // Короткое название расширения (~12 символов) для тех мест, где мало места для вывода полного названия

Просто так от Хрома ничего не получить — нужны разрешения. Что же, прочим их:

"permissions": [
  "tabs", // Доступ к объекту Tabs
  "activeTab" // Доступ к объекту ActiveTabs
],

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

Помимо прочего, нужно указать, что наше приложение хочет иметь иконку справа вверху:

"browser_action": {
  "default_title": "Default Title", // Заголовок, который отображается при наведении
  "default_icon": "icon48.png", // Иконка для отображения
  "default_popup": "popup.html" // страница (окно), которая будет отображаться при клике на иконку
},

Если приложение имеет страницу настроек, то её тоже следует указать:

"options_page": "options.html",

Также можно перманентно вешать на различные страницы какие-то ресурсы (css или js), в зависимости от адреса:

"content_scripts": [
    {
      "matches": [
        "http://site.com/*",
        "https://site.com/*"
       ],
      "css": [ "styles/styles.css" ]
    }
],

И, напоследок, скажу, что можно указать фоновую страницу:

"background": {
  "page": "background.html",
  "scripts": ["scripts.js"],
  "persistent": false
},

Фоновая страница — это невидимая страница, которая нужна для того, чтобы объединять все вкладки. Ссылки на ресурсы можно указывать как в файле манифеста, так и на самой странице background.html.

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

Фоновая страница – это невидимая страница, которая как раз и содержит такой скрипт. Их бывает два типа: persistent (постоянные) и event (управляемые событиями). Persistent, как можно догадаться, загружена постоянно, а event-страница загружается только при необходимости, когда возникает связанное с ней событие.

— Alexandr Kozlov

Обязательно прочитайте статьи Александра в его блоге.

Подробнее обо всех полях файла манифеста можно узнать из документации.

В нашем случае, файл манифеста будет выглядеть следующим образом:

{
  "manifest_version": 2,

  "name": "__MSG_app_name__",
  "short_name": "VKCommentBlocker",
  "description": "__MSG_app_description__",
  "version": "0.1.0",

  "default_locale": "ru",

  "permissions": [ "activeTab" ],

  "browser_action": {
    "default_icon": "icon_16.png",
    "default_title": "__MSG_browser_action__"
  },

  "icons": {
    "16": "icon_16.png",
    "48": "icon_48.png",
    "128": "icon_128.png"
  },

  "background": {
    "persistent": false,
    "page": "background.html"
  },

  "content_scripts": [
    {
      "matches": [
        "http://vk.com/*",
        "https://vk.com/*"
       ],
      "css": [ "styles/commentblocker.css" ]
    }
  ],

  "web_accessible_resources": [ "styles/commentblocker_on.css" ]
}

Из того, что ранее не рассматривалось

  • __MSG_key__ — это вариация Chrome на тему интернационализации приложений (i18n). Можно применять как в файле манифеста, так и в других файлах (даже CSS).
  • web_accessible_resources — массив путей ресурсов, которые будут впоследствии использоваться в контексте веб-страниц. Без указания в нём пути — ничего не получится использовать на страницах сайтов, если такое поведение предполагается.

Ресурсы расширения

Огромный плюс в карму Chrome — мы уже сейчас можем подключить расширение, конечно, если созданы все ресурсы, указанные в manifest.json.

Не думаю, что стоит заострять внимание на том, что содержится в файле commentblocker.css и commentblocker_on.css. Приведу лишь первый, в котором указаны все селекторы, в которых заключены комментарии:

@charset "utf-8";

.wall_module .reply_link_wrap .reply_link {
  visibility: hidden !important;
}

.wall_module .replies_wrap,
#wl_replies_wrap,
#wl_reply_form_wrap,
#mv_comments_wrap,
#mv_your_comment,
#pv_comments,
#pv_comments_header,
#pv_your_comment {
  display: none !important;
  visibility: hidden !important;
}

body:after {
  position: fixed;
  content: "__MSG_mode_enable__";
  top: 5px;
  right: 5px;
  padding: 6px 12px;
  background-color: #ffc;
  border: 1px solid #ddd;
  z-index: 9999;
}

В файле commentblocker_on.css, как не трудно догадаться, всё наоборот. Обратите внимание, что прямо в CSS я использую строку с языковом ключом content: "__MSG_mode_enable__". Самое время создать такой файл, где эти ключи будут храниться.

В корне нашего расширения создаём директорию _locales и вложенные в неё директории en и ru. Далее в файле messages.json описываем наши ключи.

{
  "app_name": {
    "message": "VK Comment Blocker"
  },
  "app_description": {
    "message": "Удобный способ скрыть комментарии в новостной ленте и группах."
  },
  "browser_action": {
    "message": "Переключить вид комментариев"
  },
  "mode_enable": {
    "message": "Без комментариев!"
  },
  "mode_disable": {
    "message": "С комментариями!"
  }
}

Помимо поля message есть и другие поля, о которых можно узнать из документации.

Теперь создаём файлы background.html, для начала так:

<!doctype html>
<html>
  <head>
    <title>Background</title>
  </head>
  <body>
  </body>
</html>

Тут всё так, как в обычном HTML — ничего необычного. Кстати, файл background.html можно не создавать, так как он генерируется автоматически, на основе полей в manifest.json.

Запускаем расширение

Запустить расширение можно, не написав ни одной строчки JavaScript. Чтобы сделать это, нужно пройтись по меню:

  • Настройка и управление Google Chrome (Гамбургер)
  • Дополнительные инструменты
  • Расширения
  • Поставить галочку напротив «режим разработчика»
  • Загрузить распакованное расширение
  • Выбрать папку с расширением

Расширение загрузилось и показалось в меню. Да, да, это вот это вот «В».

Chrome Extension — первый запуск

Казалось бы, у только что созданного нами расширения ничего нет в голове (нет никакой логики), а все комментарии на страницах социальной сети на букву «В» теперь скрыты. Ответ кроется в manifest.json, где в поле "content_scripts": {} мы указали на каких страницах (http://vk.com/* и https://vk.com/*) будет автоматически подключаться файл commentblocker.css, который и скрывает все комментарии. Советую подробнее почитать про mathes patterns. Он лишь с виду так прост, а под капотом чуть ли не сивый мерин, да с прибамбасами.

Chrome Extension — Комментариев больше нет

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

Оживляем расширение

Осталось выполнить второй пункт задачи, а именно реализовать возможность отображения комментариев. Вкратце, нам нужно запихнуть файл commentblocker_on.css, который отменит правила файла commentblocker.css. И тут к нам на помощь спешит наш всемогущий JavaScript.

Помните, что я говорил про background.html? Да, да, про то, что его можно не создавать. Давайте слегка изменим manifest.json:

...
"background": {
  "persistent": false,
  "scripts": [ "scripts/commentblocker.js" ]
},
...

Просто подключили JavaScript файл. Ничего особенного. Переходим к этому файлу.

Просто так запихнуть JS на страницу нельзя. И такая же проблема имеется не только со скриптами. Поэтому нам нужно воспользоваться специальной инъекцией executeScript.

Сначала нужно добавить обработчик события клика на иконку расширения:

chrome.browserAction.onClicked.addListener(function(tab) {
  chrome.tabs.executeScript(tab.id, {
    code: "(" + toggleComments.toString() + ")();"
  });
});

Где toggleComments — это функция, которая и будет производить инъекцию нашего CSS файла на страницу:

var toggleComments = function() {
  var extensionLink;

  (document.getElementById("extension") == null) ?
    (
      extensionLink = document.createElement("link"),
      extensionLink.href = chrome.extension.getURL("/styles/commentblocker_on.css"),
      extensionLink.id = "extension",
      extensionLink.type = "text/css",
      extensionLink.rel = "stylesheet",
      document.getElementsByTagName("head")[0].appendChild(extensionLink)
    )
  : (document.getElementsByTagName("head")[0].removeChild(document.getElementById("extension")))
};

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

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

  • onCreated — создание вкладки.
  • onUpdated — обновление вкладки.
  • onRemoved — закрытие вкладки.

Стоит заметить, что событие onUpdated вызывается дважды:

  • Обновление страницы;
  • Полная загрузка страницы;

На StackOverflow советуют проверять статус страницы:

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
  if (changeInfo && changeInfo.status === 'complete') {
    ...
  }
});

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

Выводы

Как нельзя кстати, стоит упомянуть мою полную версию расширения VK Comment Blocker, которая доступна в Chrome Web Store. Помимо этого, так же доступен полный исходный код на GitHub.

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

Ссылки

А как же без дополнительного материала? Думали, что я вот так просто вас отпущу?

Почему, если перейти по ссылкам указанным в конце твоей статьи, можно узнать намного больше и подробнее о том, о чем ты рассказывал в своей статье?

Ответ очень прост:

Мои статьи не должны давать тебе полные знания от и до, иначе какой смысл в том, что ты прочитал? Ты не захочешь узнать больше, если я дам тебе ответы на все твои вопросы в этой статье. А если я тебя обману или дам плохой совет, который ты примешь как должное? Шучу.

На самом деле, я не люблю углубляться в своих статьях из-за того, что их попросту будет не интересно читать. Моё дело показать, что можно сделать используя ту, или иную технологию, как это просто сделать и какие вообще есть причины рассматривать или изучать то, о чём шла речь в статье. Конечно, есть темы, которые мне очень интересны и я рассказываю о них намного больше, чем просто «обзорно».